<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.4.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">
  <meta http-equiv="Cache-Control" content="no-transform">
  <meta http-equiv="Cache-Control" content="no-siteapp">
  <meta name="baidu-site-verification" content="code-Qk3hgFvTdc">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"gitee.com","root":"/","scheme":"Gemini","version":"7.8.0","exturl":true,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":true},"copycode":{"enable":true,"show_result":true,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":true,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":"disqus","storage":true,"lazyload":false,"nav":{"disqus":{"text":"Load Disqus","order":-1}}},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="https:&#x2F;&#x2F;gaofee.cc&#x2F; 最全面试笔记-附代码照着读都能拿高薪Java基础抽象、继承、封装、多态">
<meta property="og:type" content="article">
<meta property="og:title" content="最全面试笔记-附代码照着读都能拿高薪">
<meta property="og:url" content="https://gitee.com/feege/feege.git/2022/08/01/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/index.html">
<meta property="og:site_name" content="云记">
<meta property="og:description" content="https:&#x2F;&#x2F;gaofee.cc&#x2F; 最全面试笔记-附代码照着读都能拿高薪Java基础抽象、继承、封装、多态">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps1.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps2.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps3.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps4.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps5.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps6.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps7.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps8.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps9.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps10.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps11.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps12.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps13.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps14.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps15.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps16.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps17.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps18.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps19.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps20.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps21.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps22.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps23.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps24.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps25.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps26.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps27.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps28.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps29.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps30.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps31.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps32.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps33.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps34.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps35.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps36.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps37.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps38.png">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps39.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps40.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps41.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps42.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps43.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps44.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps45.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps46.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps47.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps48.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps49.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps50.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps51.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps52.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps53.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps54.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps55.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps56.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps57.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps58.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps59.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps60.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps61.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps62.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps63.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps64.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps65.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps66.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps67.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps68.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps69.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps70.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps71.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps72.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps73.png">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps74.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps75.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps76.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps77.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps78.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps79.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps80.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps81.png">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps82.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps83.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps84.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps85.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps86.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps87.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps88.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps89.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps90.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps91.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps92.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps93.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps94.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps95.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps96.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps97.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps98.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps99.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps100.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps101.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps102.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps103.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps104.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps105.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps106.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps107.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps108.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps109.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps110.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps111.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps112.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps113.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps114.jpg">
<meta property="og:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps115.jpg">
<meta property="article:published_time" content="2022-08-01T03:20:34.547Z">
<meta property="article:modified_time" content="2023-03-21T00:23:54.799Z">
<meta property="article:author" content="云记">
<meta property="article:tag" content="Java,面试,offer,技术,微服务,分布式,中间件,数据库,bat">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://gitee.com/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps1.jpg">

<link rel="canonical" href="https://gitee.com/feege/feege.git/2022/08/01/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>最全面试笔记-附代码照着读都能拿高薪 | 云记</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">云记</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">云记</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类<span class="badge">5</span></a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档<span class="badge">12</span></a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

    </div>
  </div>

</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>
  <div class="reading-progress-bar"></div>
  <a role="button" class="book-mark-link book-mark-link-fixed"></a>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://gitee.com/feege/feege.git/2022/08/01/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="云记">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="云记">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          最全面试笔记-附代码照着读都能拿高薪
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2022-08-01 11:20:34" itemprop="dateCreated datePublished" datetime="2022-08-01T11:20:34+08:00">2022-08-01</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E9%9D%A2%E8%AF%95/" itemprop="url" rel="index"><span itemprop="name">面试</span></a>
                </span>
            </span>

          
            <span id="/2022/08/01/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/" class="post-meta-item leancloud_visitors" data-flag-title="最全面试笔记-附代码照着读都能拿高薪" title="阅读次数">
              <span class="post-meta-item-icon">
                <i class="fa fa-eye"></i>
              </span>
              <span class="post-meta-item-text">阅读次数：</span>
              <span class="leancloud-visitors-count"></span>
            </span>
  
  <span class="post-meta-item">
    
      <span class="post-meta-item-icon">
        <i class="far fa-comment"></i>
      </span>
      <span class="post-meta-item-text">Valine：</span>
    
    <a title="valine" href="/2022/08/01/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/#valine-comments" itemprop="discussionUrl">
      <span class="post-comments-count valine-comment-count" data-xid="/2022/08/01/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/" itemprop="commentCount"></span>
    </a>
  </span>
  
  <br>
            <span class="post-meta-item" title="本文字数">
              <span class="post-meta-item-icon">
                <i class="far fa-file-word"></i>
              </span>
                <span class="post-meta-item-text">本文字数：</span>
              <span>39k</span>
            </span>
            <span class="post-meta-item" title="阅读时长">
              <span class="post-meta-item-icon">
                <i class="far fa-clock"></i>
              </span>
                <span class="post-meta-item-text">阅读时长 &asymp;</span>
              <span>35 分钟</span>
            </span>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <p><span class="exturl" data-url="aHR0cHM6Ly9nYW9mZWUuY2Mv">https://gaofee.cc/<i class="fa fa-external-link-alt"></i></span></p>
<h1 id="最全面试笔记-附代码照着读都能拿高薪"><a href="#最全面试笔记-附代码照着读都能拿高薪" class="headerlink" title="最全面试笔记-附代码照着读都能拿高薪"></a>最全面试笔记-附代码照着读都能拿高薪</h1><h2 id="Java基础"><a href="#Java基础" class="headerlink" title="Java基础"></a>Java基础</h2><p>抽象、继承、封装、多态</p>
<span id="more"></span>

<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps1.jpg" alt="img"> </p>
<p>Object类的方法</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps2.jpg" alt="img"> </p>
<p>类型转换</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps3.jpg" alt="img"> </p>
<p>对象排序</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps4.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps5.jpg" alt="img"> </p>
<p>对象克隆</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps6.jpg" alt="img"> </p>
<p>序列化、反序列化</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps7.jpg" alt="img"> </p>
<p>反射（AOP、拦截器）</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps8.jpg" alt="img"> </p>
<h3 id="Hash冲突"><a href="#Hash冲突" class="headerlink" title="Hash冲突"></a>Hash冲突</h3><p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps9.jpg" alt="img"> </p>
<p>常量池（方法区）</p>
<p>拼接的字符串 或者Integer，缓存在常量池中 Integer 的区间 -128 ~ 127 之间是在常量池中</p>
<p>泛型</p>
<p>在编译期有效（类型擦除）有泛型类、泛型方法、泛型接口，参数化类型</p>
<p>集合类中都是泛型类</p>
<p>list.add(1); // 集合类中不能存基本数据类型 1 其实是进行自动装箱 包装为Integer</p>
<p>自动装箱、拆箱，基本数据类型会自动转化为包装类</p>
<p>基本数据类型</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps10.jpg" alt="img"> </p>
<p>访问权限</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps11.jpg" alt="img"> </p>
<p>结束循环（foreach无法通过 continue、break、return结束）</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps12.jpg" alt="img"> </p>
<p>异常</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps13.jpg" alt="img"> </p>
<p>Error：堆栈溢出异常，比如：递归层次太深会导致栈溢出；内存泄漏或并发量太大会造成堆溢出（内存泄漏会造成内存溢出）</p>
<p>Exception：分为运行时异常和编译时异常</p>
<p>常见的异常 文件未找到、SQL异常、连接超时、类型转换异常</p>
<p>处理异常的方式：</p>
<p>\1. try{}catch{}finally{}</p>
<p>\2. throws </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps14.jpg" alt="img"> </p>
<p>和throw的区别是：throw是主动抛出异常</p>
<p>\3. 全局异常拦截 @RestControllerAdvice 使代码简洁，程序没办法处理到所有的异常，前端也不需要看到后台的异常</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps15.jpg" alt="img"> </p>
<p>\3. 自定义异常 继承RuntimeException 或者是Exception </p>
<p>自定义异常主要是为了方便的定义错误</p>
<p>注解、元注解</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps16.jpg" alt="img"> </p>
<p>Target：声明注解的使用范围（类、方法、属性、参数）</p>
<p>Retention：声明注解的生效（编译时、运行时）</p>
<p>Documented：是否生成Api文档</p>
<p>注解就是一个标签，项目中使用到了很多的自定义注解，如果要注解起作用需要拦击器或者Aop进行处理</p>
<p><strong>动态代理–静态代理（代理模式）</strong></p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps17.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps18.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps19.jpg" alt="img"> </p>
<p><span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMyNTMyMzIxL2FydGljbGUvZGV0YWlscy84MTg3NDk5MA==">java动态代理详解_独在黑夜看湖面的博客-CSDN博客_java动态代理<i class="fa fa-external-link-alt"></i></span></p>
<p><span class="exturl" data-url="aHR0cHM6Ly93d3cuamlhbnNodS5jb20vcC85YmNhYzYwOGM3MTQ=">JAVA动态代理 - 简书 (jianshu.com)<i class="fa fa-external-link-alt"></i></span></p>
<p>Spring框架aop的原理就是动态代理</p>
<p>动态代理实现方式有两种 </p>
<p>\1. jdk动态代理 必须实现接口才能使用</p>
<p>\2. cglib动态代理 是一个字节码技术，会生成被代理对象的子类，final类型的类不能使用cglib代理</p>
<h3 id="JDK1-8新特性"><a href="#JDK1-8新特性" class="headerlink" title="JDK1.8新特性"></a><strong>JDK1.8新特性</strong></h3><p>Lambda表达式：箭头函数</p>
<p>函数式接口：一个接口中只有一个方法 @FunctionalInterface</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps20.jpg" alt="img"> </p>
<p>stream流：主要是对集合类进行操作语法比较简单，代码好维护，可以进行过滤，排序，分组，去重，统计，limit</p>
<p><span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ2NDM0MjE5L2FydGljbGUvZGV0YWlscy8xMDkwNDkzNjk=">Stream流常用方法_TayGo的博客-CSDN博客_stream流<i class="fa fa-external-link-alt"></i></span></p>
<p>接口中可以定义默认方法：default关键字修饰，以及静态方法</p>
<p>hashmap优化添加了红黑树，jvm分代持久代换成元数据区</p>
<p><strong>关键字</strong></p>
<p>Final：修饰类、修饰方法、修饰属性，类不能被继承，方法不能被重写，变量只能被赋值一次，不能修改引用，能提高代码性能（内存不会改变）</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps21.jpg" alt="img"> </p>
<h3 id="集合类（数据结构）"><a href="#集合类（数据结构）" class="headerlink" title="集合类（数据结构）"></a><strong>集合类（数据结构）</strong></h3><p><span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2dlamlhbmdibzIyMi9hcnRpY2xlL2RldGFpbHMvODE1NDA2MTY=">https://blog.csdn.net/gejiangbo222/article/details/81540616<i class="fa fa-external-link-alt"></i></span><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps22.jpg" alt="img"></p>
<p>   常见的数据结构就是数组，链表，队列，二叉树，红黑树，堆，栈这些，java的集合类顶层是collection接口，实现了他的接口有list，set，hashmap是实现了map接口，他们都实现了迭代接口itertor，常用到的还有一个collections，他是一个工具类，提供类对集合类的操作，比如说排序，查找，替换。</p>
<p>   集合类list存放的是有序，可重复的元素，set存放的是无序不可重复的，map存放的是key，value形式数据，list实现类有ArrayList，LinkedList，set的实现类就是hashset，map的实现类用过的有hashmap，LinkedHashMap。</p>
<p>   ArrayList和LinkedList的区别：ArrayList底层实现是一个数组，按照下标随机遍历比较快，因为他分配的是一个连续的存储空间，LinkedList是一个链表，增删比较快，遍历比较慢，他有一个数据节点，他指向了上一个和下一个节点的对象引用，LinkedList是一个双向链表。</p>
<p>   我们通常用到的都不是线程安全的，线程安全的集合类的话有这hashtable，vector，这些在实际项目里也没有用到过，我点过代码看过他，就是通过synchronized给方法加了一个锁实现线程安全，线程安全的集合类我们用到过currectHashmap（websocket跟客户端链接保存了客户端实例，spring的ioc容器就是用的currectHashmap实现的）</p>
<h3 id="Hashmap"><a href="#Hashmap" class="headerlink" title="Hashmap"></a><strong>Hashmap</strong></h3><p>数组+链表+红黑树来实现，是在jdk1.8之后加了红黑树，我对hashmap的理解也不是太多，我理解的就是他那个put方法，比方说我们put一个数据到这个hashmap，他就根据我们存储的key进行一个hash运算，通过这种模运算得到数组的下标，然后存储在数组的位置，因为hash有冲突，所以在数组下标的位置可能存储多个对象，所以变成链表结构，如果链表的长度大于等于8的话会树，因为链表的时间复杂度是o（n），他解决的主要是查询效率的问题，所以变成了红黑树；变成红黑</p>
<p>其实这个hashmap这个数据结构主要是查询效率的问题，无论我们是put元素或者是get元素其实的话都要找到这个元素在hashmap里是否存在，其实都要查找，查找的时候是根据这个key遍历数组，链表，还有红黑树，通过hashcode和equals方法去找这个元素是否存在，他先判断hashcode如果hashcode相等的话，然后他在进一步判断equals，因为equals的效率比较低，他比较的是内容。</p>
<p>Hashmap的话他这个扩容他的负载因子是0.75，初始大小默认是16，当他达到16*0.75=12的时候进行扩容，新建一个数组然后对数组进行copy，无论我们给hashmap初始化的大小是多少，都会变成2的多少次方这种数量，他只是改变了原有数组位置。我们为了提升hashmap这个效率在使用的时候可以避免扩容，通过构造给他初始化一个预定大小的值避免扩容。</p>
<p>Hashmap是线程安全的吗？为什么不安全呢？</p>
<p>多个线程修改一个变量的时候他会线程不安全，多个线程同时去put元素。</p>
<p>Spring</p>
<h2 id="设计模式"><a href="#设计模式" class="headerlink" title="设计模式"></a><strong>设计模式</strong></h2><p><strong>创建型模式：</strong>工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。</p>
<p><strong>结构型模式：</strong>适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。</p>
<p><strong>行为型模式：</strong>策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。</p>
<p><strong>原则：</strong></p>
<p><strong>单一职责</strong></p>
<p>​    每个类只负责自己领域中的职责，就一个类而言，应该只有一个引起它变化的原因。</p>
<p><strong>里氏替换</strong></p>
<p>​    所有引用基类（父类）的地方必须能透明地使用其子类的对象。</p>
<p><strong>开闭原则</strong></p>
<p>​    对扩展开放、对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展</p>
<p><strong>迪米特法则（最少知识原则）</strong></p>
<p>​    一个软件实体应当尽可能少地与其他实体发生相互作用。（减少耦合）</p>
<p><strong>接口隔离</strong></p>
<p>​    使用多个专门的接口，而不使用单一的总接口，即客户端不应该依赖那些它不需要的接口。当一个接口太大时，我们需要将它分割成一些更细小的接口。</p>
<p><strong>依赖倒置</strong></p>
<p>​    高层模块不应该依赖低层模块，两者都应该依赖其抽象；抽象不应该依赖细节，细节应该依赖抽象，其核心思想是：要面向接口编程，不要面向实现编程。</p>
<p>设计模式的原则有单一职责…开闭原则…接口隔离…依赖倒置…，<strong>单一职责</strong>的话，我理解就是一个类或者一个方法，只干一个事情，职责单一，我们封装的类或者方法是明确的，是可以一句话说清楚的，否则方法的可用性低；<strong>开闭原则</strong>就是对扩展开放、对修改关闭，在拓展功能的时候不应该修改已有代码，例子：文件上传、正常下单、下单优惠、使用积分，应该有不同的分支逻辑去实现，不应该影响原有功能；<strong>接口隔离原则</strong>：两个接口之间不应该有耦合，互不影响，不应该修改一个接口而影响另一个接口的可用性；<strong>依赖倒置</strong>：我们的架构一般是分层的比如说：基础层，中台服务，应用层，顶层依赖于下层，他们不能互相依赖，我们面向接口编程也不应该依赖于实现，应该依赖于接口，开发的时候要面向接口编程，不要面向实现编程；还有一个什么叫…<strong>里氏什么的</strong>… 我理解的就是咱们的接口，父类，抽象类的引用都可以指向子类对象。</p>
<p>其实设计模式我理解的就是把开发中总结出来的一套方法，大量的应用于项目中，比如MVC也是一种经典的设计模式，它实现了业务逻辑和展示逻辑的分离，比如spring框架也大量使用了设计模式，ioc是一个工厂模式，它默认的bean是单例模式，也可以通过scope这个注解改成多例模式（原型模式）；aop是代理模式；我自己写过的设计模式很少，我觉得单例模式也没有怎么写过，主要是项目中的配置类或者是工具类，我们有时候也注入到ioc容器中，通过configration这种注解或者bean注解，它本身就是一个单例模式（他应该也是一个代理模式，我们在debug代码的时候看一下实例，他就是一个jdk代理或者是cglib代理）。</p>
<p>单例模式：饿汉式、懒汉式，还有其他的构建方式记不太清了；饿汉式就是在类中通过静态变量定义了一个实例，在加载类的时候就实例化了；懒汉式的有线程安全的问题，多线程并发去获取类实例的时候会造成多次new对象，我们通过双检锁来保证一个类只有一个实例，如果实例等于null我们加synchronized代码块，然后锁定class，在代码块中再判空，然后再去new实例，如果不等于空直接返回类的实例了。</p>
<p>有个问题：通过反射是可以new实例的，枚举实现的这种单例模式应该可以避免反射创建对象的（这个我也在看过，但是还是没看明白，看的时间太长了不太记得了）。</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps23.jpg" alt="img"> </p>
<p>文件上传服务：</p>
<p>我们项目中有这种文件服务、或者消息服务这种通用的业务场景，我们一般把他们封装成各位微服务模块，文件服务承担的角色就是一个代理；代理模式的话就是代理类和被代理的对象实现了同一个接口，手写代码的话，一般是通过代理类的构造方法将实现类给到代理类。</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps24.jpg" alt="img"> </p>
<h3 id="构建者模式："><a href="#构建者模式：" class="headerlink" title="构建者模式："></a><strong>构建者模式</strong>：</h3><p>就是lombok有一个注解通过级联的方式去set属性，如果正规的写法是通过静态内部类的build方法来实现的</p>
<p><span class="exturl" data-url="aHR0cHM6Ly96aHVhbmxhbi56aGlodS5jb20vcC81ODA5MzY2OQ==">秒懂设计模式之建造者模式<i class="fa fa-external-link-alt"></i></span></p>
<p>策略模式：</p>
<p>….略</p>
<p><span class="exturl" data-url="aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3MvRHJfaXZZV0NiUEtMZDFDcjdXZHBjUQ==">电商系统设计模式实战 (qq.com)<i class="fa fa-external-link-alt"></i></span></p>
<p><span class="exturl" data-url="aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3MvRHJfaXZZV0NiUEtMZDFDcjdXZHBjUQ==">https://mp.weixin.qq.com/s/Dr_ivYWCbPKLd1Cr7WdpcQ<i class="fa fa-external-link-alt"></i></span>（电商系统设计模式实战）</p>
<p>项目中常见的设计模式，有单例、原型（也就是多例）、工厂、代理模式、策略模式、建造者模式，设计多分为创建型模式、结构型模式、行为型模式</p>
<h2 id="Java基础之多线程"><a href="#Java基础之多线程" class="headerlink" title="Java基础之多线程"></a><strong>Java基础之多线程</strong></h2><p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps25.jpg" alt="img"> </p>
<p>实现方式：</p>
<p>\1. 继承Thread类</p>
<p>\2. 实现Runnable接口</p>
<p>\3. 实现Callable接口</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps26.jpg" alt="img"> </p>
<p>其实Thread类是实现了Runnable接口，提供接口主要是为了代码的可重用性</p>
<p>Callable接口有返回值，返回值可以通过泛型指定。</p>
<p>线程状态：</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps27.jpg" alt="img"> </p>
<p>new Thread之后只有通过start方法才能启动线程进入就绪状态，然后要获得cpu的切片，然后进入运行状态，运行过程中调用wait或者sleep或者等待synchronized锁时候进入阻塞状态，在变成就绪状态等待获取cpu的切片，当run方法或者call方法执行完之后线程死亡。</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps28.jpg" alt="img"> </p>
<p>Wait和notify的使用方式：必须包含在synchronized代码块中。</p>
<p>线程之间的通信方式</p>
<p>Join</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps29.jpg" alt="img"> </p>
<p>方式一：</p>
<p>wait();notify();</p>
<p>一个线程调用了wait方法之后就放弃了cpu的使用权，sleep是没有放弃，只是等待。被wait的方法需要被notify()唤醒才可以继续执行。</p>
<p>方式二：CountDownLatch</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps30.jpg" alt="img"> </p>
<p>方式三：信号量</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps31.jpg" alt="img"> </p>
<h3 id="线程锁"><a href="#线程锁" class="headerlink" title="线程锁"></a><strong>线程锁</strong></h3><p>多线程环境下，涉及到共享资源（也就是基本数据类型或者对象或者集合类）就存在线程安全的问题。<strong>Java实现的两种方式：</strong></p>
<p>\1. Synchronized锁</p>
<p><span class="exturl" data-url="aHR0cHM6Ly93d3cuY25ibG9ncy5jb20vdGhyZWUtZmlnaHRlci9wLzE0Mzk2MjA4Lmh0bWw=">synchronized详解 - 三分恶 - 博客园 (cnblogs.com)<i class="fa fa-external-link-alt"></i></span></p>
<p><strong>修饰实例方法:</strong> 作用于当前对象实例加锁，进入同步代码前要获得 <strong>当前对象实例的锁</strong></p>
<p>synchronized void method() {</p>
<p>  <em>//业务代码</em></p>
<p>}</p>
<p>synchronized void staic method() { //业务代码 }</p>
<p>synchronized(this) { //业务代码 }</p>
<p>synchronized可以用于实例方法、静态方法、代码块。</p>
<p>作用于静态方法它的锁是当前的class，也就是所有当前class的所有实例都会被锁</p>
<p>作用于普通方法时锁对象是当前实例。</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps32.jpg" alt="img"> </p>
<p>线程安全的集合类都是通过synchronized锁来保证线程安全的。</p>
<p>Hashtable，Vector</p>
<p>\2. Lock锁</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps33.jpg" alt="img"> </p>
<p>Lock是一个接口 他的实现类有ReentrantLock、读锁、写锁、读写锁</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps34.jpg" alt="img"> </p>
<p>线程中断是一种状态（线程1可以调线程2让线程2暂停）</p>
<p><strong>多线程特性：</strong></p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps35.jpg" alt="img"> </p>
<p>原子性：通过锁来保证原子性。</p>
<p>volatile来保证可见性和有序性。</p>
<p>被volatile修饰的变量多线程之间可以共享，一个线程修改了共享变量另一个线程可以看到；有序性是说被volatile修饰的类代码不会被重排，虚拟机不会对代码进行重排</p>
<p><strong>CAS算法</strong></p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps36.jpg" alt="img"> </p>
<p>CAS 包含了三个操作数：<br>CAS（Compare And Swap/Set）比较并交换，CAS 算法的过程是这样：它包含 3 个参数CAS(V,E,N)。V 表示要更新的变量(内存值)，E 表示预期值(旧的)，N 表示新值。当且仅当 V 值等于 E 值时，才会将 V 的值设为 N，如果 V 值和 E 值不同，则说明已经有其他线程做了更新，则当前线程什么都不做。最后，CAS 返回当前 V 的真实值。</p>
<h3 id="ABA-问题："><a href="#ABA-问题：" class="headerlink" title="ABA 问题："></a><strong>ABA 问题：</strong></h3><p>比如说一个线程 one 从内存位置 V 中取出 A，这时候另一个线程 two 也从内存中取出 A，并且 two 进行了一些操作变成了 B，然后 two 又将 V 位置的数据变成 A，这时候线程 one 进行 CAS 操作发现内存中仍然是 A，然后 one 操作成功。尽管线程 one 的 CAS 操作成功，但可能存在潜藏的问题。从 Java1.5 开始 JDK 的 atomic 包里提供了一个类 AtomicStampedReference 来解决 ABA 问题。</p>
<p>循环时间长开销大：</p>
<p>对于资源竞争严重（线程冲突严重）的情况，CAS 自旋的概率会比较大，从而浪费更多的 CPU 资源，效率低于 synchronized。</p>
<p>只能保证一个共享变量的原子操作：</p>
<p>当对一个共享变量执行操作时，我们可以使用循环 CAS 的方式来保证原子操作，但是对多个共享变量操作时，循环 CAS 就无法保证操作的原子性，这个时候就可以用锁。</p>
<h3 id="AQS（ReentrantLock实现原理）"><a href="#AQS（ReentrantLock实现原理）" class="headerlink" title="AQS（ReentrantLock实现原理）"></a><strong>AQS（ReentrantLock实现原理）</strong></h3><p>AbstractQueuedSynchronizer的缩写，ReentrantLock里边有一个内部类叫Sync，这个内部类是继承了AQS，还有FairSync（公平锁）和NonfairSync（非公平锁）也是ReentrantLock的内部类。JUC包都是基于CAS算法实现的，他也结合了synchronized实现的lock锁；AQS是一个双向链表，AQS核心思想是，如果被请求的共享资源空闲，那么就将当前请求资源的线程设置为有效的工作线程，将共享资源设置为锁定状态；如果共享资源被占用，就需要一定的阻塞等待唤醒机制来保证锁分配。 这个机制主要用的是CLH队列的变体实现的，将暂时获取不到锁的线程加入到队列中。</p>
<p><span class="exturl" data-url="aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3MvMml4OXhmTHJPUnNhazZzTk1wRUVOUQ==">CAS 与 AQS 简单理解 (qq.com)<i class="fa fa-external-link-alt"></i></span></p>
<h3 id="Synchronized实现原理"><a href="#Synchronized实现原理" class="headerlink" title="Synchronized实现原理"></a><strong>Synchronized实现原理</strong></h3><p>Lock是基于cas算法的，juc包中所有东西都是基于cas算法，cas是对synchronized锁的补充；</p>
<p>Synchronized锁是基于jvm的，他可以自动加锁、释放锁，jdk1.8对他进行了优化，他有一个锁升级的过程，从无锁到偏向锁到轻量级锁最后的话是重量级锁。</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps37.jpg" alt="img"> </p>
<p>无锁：其实就是乐观锁，没有加锁，是单线程环境运行。</p>
<p>偏向锁：java对象的存储结构很复杂，他里边保存的有一个线程id，说明有线程在访问这个对象</p>
<p>轻量级锁：认为锁有竞争的情况，然后通过自旋加锁（死循环，监控线程偏向锁的线程id是否释放）</p>
<p>重量级锁：线程自旋超过了一定次数就会升级为重量级锁，其实是jvm来完成的，比较消耗性能也是jdk1.8升级锁的原因</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps38.png" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps39.jpg" alt="img"> </p>
<h3 id="原子包装类"><a href="#原子包装类" class="headerlink" title="原子包装类"></a><strong>原子包装类</strong></h3><p>线程安全的集合</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps40.jpg" alt="img"><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps41.jpg" alt="img"> </p>
<p>保证线程安全一种是使用线程安全的集合类 concurrentHashMap、原子包装类…分布式环境下java的锁会失效</p>
<h3 id="线程池"><a href="#线程池" class="headerlink" title="线程池"></a><strong>线程池</strong></h3><p>解决的问题：</p>
<p>\1. 避免重复创建线程，线程可以重复利用</p>
<p>\2. 控制线程并发，能有效利用cpu资源，项目中并不是线程越多效率越高</p>
<p>Java提供了几种定义好的线程池，他们的工作队列都是无界的，容易造成内存溢出，项目中一般使用都是自定义线程池。</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps42.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps43.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps44.jpg" alt="img"> </p>
<p>Callable接口使用submit方法，runnable接口使用execute</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps45.jpg" alt="img"> </p>
<p>自定义线程池</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps46.jpg" alt="img"> </p>
<p>工作原理：</p>
<p>当有任务提交到线程池时候先交给核心线程执行，核心线程满了之后会放入工作队列，如果队列也满了才会创建新的线程直到最大线程数，如果线程被用尽，队列也满了，会执行拒绝策略</p>
<p>拒绝策略：</p>
<p>\1. 拒绝任务，抛异常</p>
<p>\2. 拒绝任务，不抛异常</p>
<p>\3. 丢弃队列前边的任务，将新任务提交进队列</p>
<p>\4. 由调用方线程执行任务 （常用）</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps47.jpg" alt="img"> </p>
<p>项目中的场景：一个接口处理的业务逻辑越来越多接口会越来越慢，我们可以通过多线程的方式来组装接口数据（数据类型比较多）；数据量比较大；</p>
<p><span class="exturl" data-url="aHR0cHM6Ly96aHVhbmxhbi56aGlodS5jb20vcC8zNDQ0MzEzNDE=">CompletableFuture用法详解 - 知乎 (zhihu.com)<i class="fa fa-external-link-alt"></i></span></p>
<p>线程一需要线程二的结果</p>
<p>Excel导出 十万条数据，从数据库读数据，写excel</p>
<h3 id="Java基础之JVM虚拟机"><a href="#Java基础之JVM虚拟机" class="headerlink" title="Java基础之JVM虚拟机"></a><strong>Java基础之JVM虚拟机</strong></h3><p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps48.jpg" alt="img"> </p>
<h2 id="Jvm原理："><a href="#Jvm原理：" class="headerlink" title="Jvm原理："></a><strong>Jvm原理：</strong></h2><p>我们的class文件会通过类加载器加载到内存区域，由执行引擎执行我们的字节码文件，内存区域分为：堆、栈、方法区、本地方法栈、程序计数器</p>
<h3 id="jvm内存模型"><a href="#jvm内存模型" class="headerlink" title="jvm内存模型"></a><strong>jvm内存模型</strong></h3><p>方法区中存的是类信息，方法信息，还有常量池</p>
<p>栈里边存的是基本数据类型和对象引用</p>
<p>堆里存的是对象和数组</p>
<p>程序计数器是栈帧执行的位置，因为cpu是分片给每个线程的，线程之间交替执行，要知道代码执行的位置</p>
<p>本地方法栈：动态链接库的方法，java调用其他语言的一些类库，方法</p>
<p>堆是垃圾回收的主要区域</p>
<p>栈和程序计数器是线程私有的</p>
<h3 id="类加载器"><a href="#类加载器" class="headerlink" title="类加载器"></a><strong>类加载器</strong></h3><p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps49.jpg" alt="img"> </p>
<p>类加载器作用：主要是将class字节码文件加载到内存中分为：app加载器，ext加载器，bootstrap加载器 ，我们也可以自定义类加载器。</p>
<p>双亲委派原则：</p>
<p>当一个类加载的时候会讲加载请求交给app加载器，app加载器会交给ext加载器，ext加载器会交给bootstrap加载器，如果这个bootstrap和ext加载器都不能加载，才会由app加载器加载。</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps50.jpg" alt="img"> </p>
<p>类加载的过程：通过io去读class文件，虚拟机校验字节码文件是否合法</p>
<p>准备：类加载的过程为静态变量分配空间</p>
<p>解析：静态变量和引用建立关系的过程</p>
<p>初始化：静态代码快的执行，如果有父类先加载父类</p>
<h3 id="Jvm分代垃圾回收"><a href="#Jvm分代垃圾回收" class="headerlink" title="Jvm分代垃圾回收"></a><strong>Jvm分代垃圾回收</strong></h3><p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps51.jpg" alt="img"> </p>
<p><strong>分代</strong>：青年代，老年代；青年代分为：E区S0区S1区，当E区满的时候会触发垃圾回收（青年代的垃圾回收）把回收不掉的对象放入S0区，当E区再次满的时候会回收E区和S0区把回收不掉的对象放入S1区，经过多次回收不掉的话会进入老年代，老年代如果满了之后也会触发垃圾回收（老年代垃圾回收FULL GC）我们所谓的jvm优化都是避免full gc，使垃圾在青年代都回收完成。</p>
<p>青年代垃圾回收时间很短，老年代回收时间比较长，我们可以通过jvm参数调整他们之间的比例</p>
<p>青年代使用复制算法，老年代使用标记整理算法</p>
<p><strong>垃圾回收算法</strong>：</p>
<p>引用计数法，可达性分析法，标记整理法，标记清除法，复制算法</p>
<p>标记清除法：有内存碎片，会造成内存空间不连续</p>
<p>标记整理算法：会整理内存碎片</p>
<p>复制算法：效率高，会浪费内存空间</p>
<p><strong>项目中有两种</strong>：cms（多线程标记清除算法），g1（基于标记-整理算法，不会产生内存碎片）</p>
<p>Cms执行过程：初始标记–并发标记–重新标记–并发清除</p>
<p>G1的特点是：把内存区域分成若干块，逐块回收，能够精确的控制垃圾回收的时间</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps52.jpg" alt="img"> </p>
<p><strong>Jvm调优</strong></p>
<h2 id="Spring框架"><a href="#Spring框架" class="headerlink" title="Spring框架"></a><strong>Spring框架</strong></h2><p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps53.jpg" alt="img"> </p>
<p>核心模块：IOC其他模块都依赖于IOC，上下文模块可以和IOC容器交互 ApplicationContext，SpringMVC（Web），ORM持久层操作，事务管理</p>
<h3 id="IOC"><a href="#IOC" class="headerlink" title="IOC"></a><strong>IOC</strong></h3><p>IOC其实是一个Bean工厂，它管理了所有Bean以及它们之间的依赖，Spring容器在启动的时候，读取xml或注解，通过反射生成Bean，通过@Autowired注入Bean。</p>
<p>IOC容器也叫控制反转，控制权和使用权分离，在没有IOC容器的情况下使用对象要new对象，现在IOC容器管理了对象和对象之间的依赖注入，我们只管使用，通过IOC的方式实现解耦（面向接口编程依赖于接口而不是具体的实现）</p>
<p><strong>Bean生命周期</strong></p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps54.jpg" alt="img"> </p>
<p>Spring Bean的生命周期分为四个阶段：实例化 Instantiation –&gt; 属性赋值 Populate –&gt; 初始化 Initialization –&gt; 销毁 Destruction</p>
<p><strong>实例化</strong>：所谓的初始化就是创建对象实例，spring框架在启动时候会通读取xml文件或者通过反射去读取Bean注解来创建对象实例</p>
<p><strong>依赖注入</strong>：通过构造或者set方法注入依赖，项目中用到的@autowried和@resource关键字</p>
<p><strong>初始化</strong>：@PostConstruct（被注解的方法，在对象加载完依赖注入后执行）</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps55.jpg" alt="img"> </p>
<p><strong>销毁</strong>：默认单例，在项目结束时候会销毁对象实例，我们也可以改为多例模式</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps56.jpg" alt="img"> </p>
<p>Spring框架为我们提供了一些生命周期的接口</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps57.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps58.jpg" alt="img"> </p>
<p><span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RlZmluZV91cy9hcnRpY2xlL2RldGFpbHMvODAwNjAzODQ=">Spring生命周期中的常见接口_define_us的博客-CSDN博客_spring 生命周期接口<i class="fa fa-external-link-alt"></i></span></p>
<p><strong>作用域</strong>：</p>
<p>单例，多例（原型），request，session，application</p>
<p><strong>循环依赖：</strong></p>
<p><span class="exturl" data-url="aHR0cHM6Ly93d3cuY25ibG9ncy5jb20vZGFpbXpoL3AvMTMyNTY0MTMuaHRtbA==">https://www.cnblogs.com/daimzh/p/13256413.html<i class="fa fa-external-link-alt"></i></span></p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps59.jpg" alt="img"> </p>
<p>出现循环依赖是代码结构不合理，不应该出现这样的问题，代码不好维护</p>
<p>通过三级缓存和set注入，延迟注入来解决循环依赖</p>
<h3 id="AOP"><a href="#AOP" class="headerlink" title="AOP"></a><strong>AOP</strong></h3><p>AOP（Aspect-Oriented Programming，面向切面编程）能够将那些与业务无关，为业务模块共同需要的功能（例如事务处理、日志管理、权限控制等）封装起来，便于减少系统的重复代码，降低模块间的耦合度，并有利于未来的可扩展性和可维护性。</p>
<p>AOP是一个代理模式，支持jdk代理和cglib代理，我们可以拦截controller和service 的方法来实现某些业务功能的封装，它提供了前置通知、后置通知、环绕通知、异常通知等通知类型，我们在使用aop的时候首先定义切面类，定义切点，使用不同的通知类型来处理我们的业务</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps60.jpg" alt="img"><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps61.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps62.jpg" alt="img"> </p>
<p><strong>SpringMvc</strong></p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps63.jpg" alt="img"> </p>
<p>MVC 是一种设计模式，实现了业务逻辑和展示逻辑的分离。</p>
<p>（1）客户端（浏览器）发送请求，直接请求到 DispatcherServlet。</p>
<p>（2）DispatcherServlet 根据请求信息调用 HandlerMapping，解析请求对应的 Handler。</p>
<p>（3）解析到对应的 Handler（也就是我们平常说的 Controller 控制器）后，开始由 HandlerAdapter 适配器处理。</p>
<p>（4）HandlerAdapter 会根据 Handler 来调用真正的处理器开处理请求，并处理相应的业务逻辑。</p>
<p>（5）处理器处理完业务后，会返回一个 ModelAndView 对象，Model 是返回的数据对象，View 是个逻辑上的 View。</p>
<p>（6）ViewResolver 会根据逻辑 View 查找实际的 View。</p>
<p>（7）DispaterServlet 把返回的 Model 传给 View（视图渲染）。</p>
<p>（8）把 View 返回给请求者（浏览器）</p>
<p><span class="exturl" data-url="aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3MvYko4ZHZzX3FDQTNDYmpaWEhZckVmUQ==">https://mp.weixin.qq.com/s/bJ8dvs_qCA3CbjZXHYrEfQ<i class="fa fa-external-link-alt"></i></span></p>
<p>Springmvc有个核心控制器叫DispatcherServlet，它负责接收所有的请求，它将请求分发给我们写的controller，controller进行业务逻辑处理，返回Model AndView给DispatcherServlet，</p>
<p>DispatcherServlet调用视图解析的组件渲染成html给前端返回。</p>
<p>Mvc是一个很经典的设计模式实现了业务逻辑和展示逻辑的分离，也就是查数据的逻辑和展示的逻辑分离，如果没有mvc，java是通过servlet实现的。Mvc实现了解耦，提高开发效率</p>
<p><strong>事务管理</strong></p>
<p>Spring框架帮我们管理了事务，一般分为手动事务和声明式事务，项目中一般使用的是声明式事务，声明式事务分两种：一种是xml配置，一种是@Transactional注解</p>
<p>持久层框架使用的是mybatis\mybatis-plus</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps64.jpg" alt="img"> </p>
<p>关系型数据库可以开启事务，提交事务</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps65.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps66.jpg" alt="img"> </p>
<p><strong>事务注解</strong></p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps67.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps68.jpg" alt="img"> </p>
<p>事务失效</p>
<p>非运行时异常会造成事务失效，所以我们在注解上加入rollbackfor指定为exception.class</p>
<p>Try{}catch{}拦截了异常之后也会造成事务失效</p>
<p>非public修饰的方法会事务失效</p>
<p>使用this关键字（aop代理失效），可以通过AopContext.currentProxy();获取当前代理类去调用</p>
<p><strong>Springboot</strong></p>
<p>Springboot能快速创建项目，它内嵌了servlet容器，它提供默认配置，我们可以通过修改application配置文件，更改默认配置。它提供很多starter应用场景，几乎包含所有的常见框架，redis、es、rabbitmq的starter，使我们更加快速使用第三方框架。Springboot是去xml化，我们通过Bean、Configration注解来注入Bean到SpringIOC容器。Springboot帮我们管理了依赖和版本，避免了集成项目的jar包冲突，可以提高开发效率。</p>
<p>&lt;spring-boot.version&gt;2.3.4.RELEASE&lt;/spring-boot.version&gt;<br>&lt;spring-cloud.version&gt;Hoxton.SR8&lt;/spring-cloud.version&gt;</p>
<p>Springboot项目只改变了spring框架是使用方式，意味着spring，springmvc的代码都没有改变</p>
<p><strong>自动配置</strong></p>
<p>@EnableAutoConfiguration</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps69.jpg" alt="img"> </p>
<p>自动配置在启动类上有一个@SpringBootApplication注解 这个注解上有个元注解@EnableAutoConfiguration来开启自动配置；每个start下面都有一个spring.factories文件，springboot项目在启动时候会扫描这个文件，实例化配置类到IOC容器；springboot开发了一些条件注解：@ConditionalOnProperty @ConditionalOnClass @ConditionalOnBean</p>
<p>@ConditionalOnProperty：配置文件中有指定的配置文件时候才会生效</p>
<p>@ConditionalOnClass：在加了一个依赖时候才会生效</p>
<p>@ConditionalOnBean：在IOC容器中有指定Bean时候生效</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps70.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps71.jpg" alt="img"> </p>
<p><strong>常用注解</strong></p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps72.jpg" alt="img"> </p>
<p>Mvc注解</p>
<p>@Controller（@RestController）</p>
<p>@RequestMapping（@GetMapping、@PostMapping、@PutMapping、@DeleteMapping）</p>
<p>@ResponseBody （@RequestBody）</p>
<p>@PathVariable路径注解（@RequestParam）</p>
<p>Spring常用注解</p>
<p>@Service（@Repository，@Controller）</p>
<p>@Configration</p>
<p>@Bean</p>
<p>@Component</p>
<p>@Autowried（@Resource、@Qualifier、@Primary）</p>
<h2 id="SpringCloud"><a href="#SpringCloud" class="headerlink" title="SpringCloud"></a><strong>SpringCloud</strong></h2><p>常用组件：注册中心，网关，配置中心，feign组件，ribbon负载均衡，hystrix熔断</p>
<p>   Skywalking、sentinel、SpringBootAdmin</p>
<p>SpringCloud常用组件的话我们项目中用到的注册中心有原生的eureka还用到过nacos来负责服务的注册与发现，再有就是gateway网关，做一些路由转发，权限校验，限流跨域之类的，再有的话就是服务间调用的feign组件集成了rabbin负载均衡和histry熔断，像其他组件的话还用到了admin监控，还有链路跟踪。</p>
<p>注册中心eureka和nacos核心功能就是服务注册与发现，nacos的话自带了一个配置中心，通过这种命名空间来管理不同环境的配置文件，开发测试还有生产，配置中心能起到一个热部署的作用，就是修改了配置文件之后不用重启项目，eureka和nacos服务注册与发现的机制都差不多，都是通过心跳的方式来保持自己的在线状态。Eureka和nacos默认都是ap，强调的是一致性和分区容错，他的配置中心是强调一致性的。</p>
<p>网关我们使用的gateway网关，相对于zuul来说性能是比较高的，使用了一种叫react的响应式编程技术，还有的话就是提供了更多的路由策略，里边有断言这个概念，断言成功之后会路由到后端的服务，比如断言支持cookid，支持ip，支持请求参数，还提供了全局过滤器，还有局部过滤器，局部过滤器就是针对于某个微服务做特殊的处理，我们使用的一般都是全局过滤器来实现权限校验和一些白名单黑名单之类的功能</p>
<p>Feign组件我理解的其实就是封装了http请求，spring框架还带了一个restemplate模板，他可以配合ribbon实现负载均衡，然后完成服务之间调用，其实feign组件是一种声明式的服务之间调用的方式，死代码的话简洁易懂，提高了可读性，当服务调用的时候他首先通过ribbon做负载均衡，选择一个服务实例然后发送http请求来完成服务之间调用的，如果我们开启了histry熔断，他会监控请求，然后统计服务调用的失败次数，统计他的失败率，当达到阈值的时候，具体不记得了，触发熔断，返回一个默认值避免雪崩效应，这个histry有半开状态，他会每5秒从大量的请求里边分发一个请求到实际微服务当中去，如果ok了就会关闭熔断状态，否则的话仍然返回固定值。</p>
<p>服务监控的话其实可以看到每个微服务的实例个数以及他们的状态，如果有微服务掉线的话我们可以设置报警，通过邮件或者其他方式通知给运维或者开发的人，还能看到每个实例的详细信息，比如他的那个堆栈信息，jdk配置，还有一些环境相关的很多东西吧</p>
<p>链路跟踪其实的话就是能够收集服务之间调用信息，然后以web界面的方式让我们能直观的看到，我们项目里边一个服务接口可能被很多服务调到，我们能够详细地看到他的调用频率，还有具体有哪些微服务去调这个接口了，提供了各种方式的这种查询，主要是它能够看到一些服务之间调用的响应比较慢的接口，可以让我们提前对有问题的接口进行提前优化，能够更好地直观的了解微服务之间的状态吧。</p>
<p>Dubbo框架和SpringCloud的区别：我觉得他的区别主要是协议不同，一个是tcp协议，一个是http协议，http协议是基于tcp的，性能来说的话比tcp要低一点，每次都有一个链接过程；而dubbo基于zookeeper强调的是一致性cp，然后eureka强调的是可用性ap，eureka只要有一个节点存在就是可用的，dubbo只要挂掉一半节点以上，整个集群是不可用的；dubbo是一个单纯的rpc框架，核心就是远程过程调用，默认使用的是dubbo协议，当然还有其他的一些协议，除了有监控中心的外没有任何其他组件了，SpringCloud提供了微服务场景下的几乎所有解决方案，组件还是比较多的，通过低代码开发就能构建完整的微服务应用；dubbo的话也在融合到SpringCloud中，现在有这个SpringCloud Alibaba，SpringCloud很多原有组件已经不提供技术支持了，我们项目逐渐使用Alibaba版本。</p>
<p>注册中心：服务注册与发现 CAP理论 Dubbo（http\ tcp\ip区别）</p>
<p>服务间调用：feign （httpclient、restTemplent模板，okhttp）接口安全，接口幂等性，超时</p>
<p>服务降级：熔断，限流</p>
<p>网关：路由转发，权限校验，黑白名单，跨域配置，缓存，限流（结合redis实现限流<span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpdWhlbmdodWk1MjAxL2FydGljbGUvZGV0YWlscy8xMTM5MzIyMzA=">限流算法<i class="fa fa-external-link-alt"></i></span>）</p>
<p>如何实现服务的平滑上线下线：注册中心提供了一些restful接口，我们可以通过调接口让某个服务实例下线</p>
<h2 id="Mysql"><a href="#Mysql" class="headerlink" title="Mysql"></a><strong>Mysql</strong></h2><p>架构</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps73.png" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps74.jpg" alt="img"> </p>
<p><span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mjk3NTU5NC9hcnRpY2xlL2RldGFpbHMvMTA2OTQyNzIy">MySQL几种日志类型_Bridge博客的博客-CSDN博客_mysql日志分为几种类型<i class="fa fa-external-link-alt"></i></span> </p>
<p>Mvcc </p>
<p><span class="exturl" data-url="aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3M/c3JjPTExJnRpbWVzdGFtcD0xNjU3ODUzMzQ3JnZlcj0zOTIxJnNpZ25hdHVyZT1rVmZreVpoOU4tRzM0TzdPd0FEMVpab3lEbjBuYzJDKmdkM0NKcmoqM0p0M2lza014ODIqdldIVkxTNHZzeVlQREZxSndhQnJnenNQT2ZOMU80dGV0NUsqRCpsNUdYZW14WERYYi1rVHduM1RZUDRBTmNEMWI1WmM1VGViVnZCbSZuZXc9MQ==">图解MVCC！ (qq.com)<i class="fa fa-external-link-alt"></i></span></p>
<p><strong>优化</strong></p>
<p><strong>表设计</strong></p>
<p>我们根据原型图去设计表，从原型图提取关键字比如楼盘，房源…其实我们是根据面向对象的思想去设计表的，抽象对象，细化他们的一个属性，一个对象对应着一个表，表之间的关系通过外键或者关系表去表示，或者是通过主键去关联一对一的场景，我们反复的去跟根据原型验证表的合理性，这样就能基本满足功能性需求，非功能性需求我们也需要考虑（公用字段、软删除，创建时间，修改时间，状态字段）特别是状态字段一般代表了流程，需要从流程的角度看表的合理性，如果表的数据量比较大还需要考虑索引或者是否需要分表操作</p>
<p>表字段</p>
<p><span class="exturl" data-url="aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3M/c3JjPTExJnRpbWVzdGFtcD0xNjU3ODU0MDQxJnZlcj0zOTIxJnNpZ25hdHVyZT1TanFabXY3Z0k0TjVydHA5ZmVMQlZ1M2FwQ3Nhb2MzbEhlUEpReVVEMGhERWNQTzUwdkFXZ0w4ajJBRXFNUERvZWNrUTM3VkVMMDFoWGc3bUcwYzlDUkVrT2RobTVSaVJITVVCM0llQzZOSkt6ZW1PaE1WZHJPWC1xUkVRdDk1VyZuZXc9MQ==">MySQL数据类型 (qq.com)<i class="fa fa-external-link-alt"></i></span></p>
<p>Char和varchar 的区别</p>
<p>Int TINYINT，SMALLINT，MEDIUMINT，INT 和 BIGINT</p>
<p>表优化：大表拆小表，选择合适的数据类型，三大范式（有主键，字段不可分割，不能有数据冗余），反范式设计（允许数据冗余）避免了多表关联查询，如果冗余的数据更新了怎么办？异步更新，延迟更新</p>
<h3 id="索引"><a href="#索引" class="headerlink" title="索引"></a><strong>索引</strong></h3><p>根据业务特点通常会给表创建索引，提高查询效率，索引会影响增删改的效率，所以一个表不能创建太多的索引，一般的话会创建唯一索引（手机号、身份证号…），组合索引（关系表，省市区分类，业务特点：常用的查询），一般会在表的外键或者是组合查询的特点创建一些组合索引。</p>
<p>索引类型</p>
<p>主键索引，唯一索引，组合索引，全文索引（myisam）</p>
<p>普通索引都有先查索引再回表取数据的过程</p>
<p>索引覆盖：查询的列都有索引，他不需要回表，效率比较高</p>
<p>聚簇索引：索引文件和数据文件是同一个文件（innodb）</p>
<p>非聚簇索引：索引文件和数据文件不是同一个文件（myisam）</p>
<p>索引的<strong>数据结构</strong>：B+Tree（叶子结点存储数据<span class="exturl" data-url="aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3M/c3JjPTExJnRpbWVzdGFtcD0xNjU3ODU1OTIwJnZlcj0zOTIxJnNpZ25hdHVyZT1yeGJRMGR2elJpUzA4Ulg0NDJQTm5xRjJmWVhPU3h2NjQ3NHRwQWQ0Kk9ZT1V5OHJndmNMZWhLS0FJYlJNRmszRVdhLTJlR1JSdmk2KmY1cSpDLXR3UjAyc0E3UEc0THpKUkk3N1RQdHZMZlVZbUdEQ3pNNzVYY1VyUFlDUHBnaSZuZXc9MQ==">什么是B+Tree<i class="fa fa-external-link-alt"></i></span>） B-Tree（结点上存了数据）</p>
<h3 id="Sql优化"><a href="#Sql优化" class="headerlink" title="Sql优化"></a><strong>Sql优化</strong></h3><p>Select * 避免使用，降低mysql的io瓶颈（后台管理系统一般没有要求，但是用户端的系统VO、DTO、PO）<span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NoOTdDS2QvYXJ0aWNsZS9kZXRhaWxzLzEyNTgzOTQ1Mw==">https://blog.csdn.net/Ch97CKd/article/details/125839453<i class="fa fa-external-link-alt"></i></span></p>
<p>Sql优化的核心是避免全表扫描，尽量使用索引，是否使用到了索引可以通过执行计划来看</p>
<p>1.最多匹配原则，组合索引要符合最左匹配原则，如果我们写的条件包含组合索引的所有列，不符合最左匹配原则的话，其实的话他也能用全索引，这个我通过执行计划来去测试过，mysql服务器内部是有这个优化机制的。</p>
<p>2.隐式转换，会造成索引失效，比方说我们数据库定义的是一个字符串类型是一个结构，在他实际的话可能是一个数字，我们没有加双引，会造成这个索引失效。</p>
<p>3.列进行了运算也会造成索引失效，比如where条件后面这个a+1=2，这时候就会索引失效，或者列运用了函数也会造成索引失效。</p>
<p>4.我们在多表联查的时候应该尽量的减少关联查询的范围，然后在on条件或者是where条件上指定他的这个数据范围，避免更多的数据关联查询。我们在这个where条件然后group by ,order by的时候尽量注意索引的使用。</p>
<p>5.像大表的分页，我们可以通过对id先分页然后经过二次查询或者关联查询，查数据，因为limit的分页随着分页增大，效率越低</p>
<p>6.我们在项目里边如果三表之上的关联查询尽量少用，我们有的时候由于工期问题也会写很大的sql，然后来完成相应的功能，属于一种没有办法的情况吧，其实大的sql对于代码的维护性是很差的，实现的时候简单可能维护的时候特别麻烦，我们有些项目里要求必须全是单表查询，然后在内存组装数据，能保证数据库的一个效率，避免数据库出现问题。</p>
<p>7.我们凭经验对sql进行编写，最终还是由执行计划通过explain去看我们这个sql是否是最优的，通过执行计划我们可以看到sql的执行顺序（id），可以看到查询的类型（select type），用到的表以及他的数据量，有哪些索引可以用，以及具体用到哪些索引以及他的索引长度。还有一个比较关键的就是一个列就是type，他明确地告诉我们这是一个什么级别的查询，最好的情况下就是常量级（system ，const ）或者是用全了索引（index）最坏的情况就是all进行了全表扫表</p>
<p>system &gt; const &gt; eq_ref &gt; ref &gt; fulltext &gt; ref_or_null &gt; index_merge &gt; unique_subquery &gt; index_subquery &gt; range &gt; index &gt; ALL</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps75.jpg" alt="img"> </p>
<p><strong>锁</strong></p>
<p><span class="exturl" data-url="aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3M/c3JjPTExJnRpbWVzdGFtcD0xNjU3ODU2NzI5JnZlcj0zOTIxJnNpZ25hdHVyZT03M1UwUzdZT3djdm51VjlKaWVoV1pKOHVtblFCS3J4MTBLa0JrWlVjNy1wOXEyWlQtYjVxd2NMTHJMQWNtTlRXZDRlN2hmWVVqUHhLKjJ0KlpYb0tDc1ZwYVpOWC00SDFQOUJvVEozY2ZvV054LUpMMHpudXUyMEtEQnEwKjZzWSZuZXc9MQ==">MySQL各种锁总结 (qq.com)<i class="fa fa-external-link-alt"></i></span></p>
<p>表锁、行锁、页锁（有其他不同的叫法）</p>
<p>侧重说mysql乐观锁 <span class="exturl" data-url="aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3M/c3JjPTExJnRpbWVzdGFtcD0xNjU3ODU3MDIwJnZlcj0zOTIxJnNpZ25hdHVyZT02Y0diYW4zTm5IM1REakVPZlV6eWxzdTZOcmRJaDVyT3FONFVqMjkqcllBQm5Fd2lEQnA5bTZJcFZEWFNUZUloZC1neEt5Q25obGk0RTZCSDdHcDRwRTlvbVBDbmJQUzNqU0t1LXhJdEMxdUloMUtiZmFlT1IyaHpabHJXNTFqRCZuZXc9MQ==">MySQL乐观锁扣减库存原理图解 (qq.com)<i class="fa fa-external-link-alt"></i></span></p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps76.jpg" alt="img"> </p>
<p>Mysql乐观锁：数据库表都有一列叫版本号，每次更新都会加1，当我们更新数据的时候先查询要更新的这条数据，取得版本号，更新数据的时候where条件后边拿版本号当做条件，如果中间被其他线程修改过了，当前线程就更新不到数据了，这就是乐观锁的实现。一般项目中如果更新失败的话我们需要重试，重试3次仍然失败的话，就返回失败。</p>
<p>Public void stock(Long spuId, Integer num){</p>
<p>  For(int i = 0; i &lt; 3; i++){</p>
<p>If(stock2(spuId,num)){</p>
<p>  // 成功</p>
<p>  return;</p>
<p>}</p>
<p>}</p>
<p>throw RuntimeException(“减库存失败”);</p>
<p>}</p>
<p>Public Boolean stock2(Long spuId, Integer num){</p>
<p>  Updata t_stock set stock_num = stock_num - num, version = version + 1 where spu_id = spuId and version = version</p>
<p>  Return i &gt; 0;</p>
<p>}</p>
<h2 id="Mybatis-（jdbc、jdbcTemplate模板、mybatis、mybatis-plus、jpa）"><a href="#Mybatis-（jdbc、jdbcTemplate模板、mybatis、mybatis-plus、jpa）" class="headerlink" title="Mybatis （jdbc、jdbcTemplate模板、mybatis、mybatis-plus、jpa）"></a><strong>Mybatis （jdbc、jdbcTemplate模板、mybatis、mybatis-plus、jpa）</strong></h2><p>ORM（对象关系映射 Object Relational Mapping）</p>
<p>解决了java对象可以直接保存成数据库的一条记录，从数据库中查出来就是java的对象，使我们操作关系型数据库更加便捷。</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps77.jpg" alt="img"> </p>
<p>Mybatis原理：</p>
<p>Mybatis是对jdbc的封装，完成java对象可以直接保存成数据库的一条记录，从数据库中查出来就是java的对象，使我们操作关系型数据库更加便捷。</p>
<p>启动时候加载全局配置以及mapper的xml文件（实体类的包名，mapper的扫描路径）；有SqlSessionFactory 和 Sqlsession，Sqlsession相当于一个connaction连接，SqlSessionFactory 是一个连接工厂</p>
<p>Mybatis通过threadlocal实现的线程安全</p>
<p>Mybatis和jpa的区别：</p>
<p>Mybatis是一个半自动的orm框架，我们可以自定义sql，jpa是基于hibernate的，是一个全自动的orm框架，我们项目中主要是使用mybatis多一点，特别是客户端的程序，需要做sql优化时候特别方便，jpa支持自动建表，支持级联操作（@ManyToOne，@ManyToMany，@OneToMany）使用的是spring-data-jpa项目中并没有级联操作，因为效率太低，使用比较局限。</p>
<p>Mybatis和mybatis-plus的区别：</p>
<p>mybatis-plus完成了单表的增删改查，项目通过反向工程生成持久层也包含了service层，baseservice封装了单表的增删改查方法，里边有basemapper，baseservice；Mybatis-plus还扩展了逻辑删除，多数据源，数据的脱敏，乐观锁。</p>
<p><span class="exturl" data-url="aHR0cHM6Ly93d3cubXliYXRpcy1wbHVzLmNvbS9ndWlkZS8=">简介 | MyBatis-Plus<i class="fa fa-external-link-alt"></i></span></p>
<p>Mybatis的插件：</p>
<p>Pagehelper插件：通过拦截器实现，拦截到要执行的sql，然后计算起始条数，拼接limit</p>
<p>常用标签：<select><delete><insert><update><where><if><case><foreach></foreach></case></if></where></update></insert></delete></select></p>
<p>Mybatis的<strong>接口不能重载</strong></p>
<p>参考资料：</p>
<p><span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3Blbmd6b25nbHU3MjkyL2FydGljbGUvZGV0YWlscy84NjYyNTQxNQ==">工具类：JDBC工具类_曹利荣的博客-CSDN博客_jdbc 工具类<i class="fa fa-external-link-alt"></i></span></p>
<h2 id="Psql（PostgreSQL）"><a href="#Psql（PostgreSQL）" class="headerlink" title="Psql（PostgreSQL）"></a><strong>Psql（PostgreSQL）</strong></h2><p>Psql、mysql、oracle的区别</p>
<p>Oracle与mysql的区别：</p>
<p>关系型数据库的Sql语句基本相同，里边的一些函数不一样，分页也不一样，oracle的分页要自己去实现，表里有个rownum；主键生成策略不同，oracle通过自定义函数生成主键，没有自增主键。</p>
<h2 id="MongoDB（在公司当做关系型数据库在使用，替代mysql）"><a href="#MongoDB（在公司当做关系型数据库在使用，替代mysql）" class="headerlink" title="MongoDB（在公司当做关系型数据库在使用，替代mysql）"></a><strong>MongoDB（在公司当做关系型数据库在使用，替代mysql）</strong></h2><p>Mongo是非关系型数据库，存储的是json格式的数据，应该是叫bjson，他扩展了一些数据类型，比如说日期，概念上来跟mysql差不多有数据库，集合，文档；</p>
<p>我们使用spring-data操作的</p>
<p><span class="exturl" data-url="aHR0cHM6Ly93d3cudzNjc2Nob29sLmNuL21vbmdvZGIvbW9uZ29kYi1kYXRhLXR5cGUuaHRtbA==">MongoDB 数据类型_w3cschool<i class="fa fa-external-link-alt"></i></span></p>
<h2 id="Redis"><a href="#Redis" class="headerlink" title="Redis"></a><strong>Redis</strong></h2><p>我们项目中使用redis还是特别多的，比如：xxxx；Redis是一个非关系型数据库，单机并发读写能达到10万左右，是一个内存数据库，单线程的（避免上下文切换），IO多路复用。</p>
<p>项目中的应用：短信验证码，图片验证码，缓存字典数据，表单重复提交，热点数据缓存（字典数据、分类数据、省市区、核心业务数据）</p>
<p>什么叫热点数据：就是楼盘，房源数据（比如最近一周更新的房源，可以通过定时任务或者手动加入；首页出现的房源，用户最近搜索过的房源）</p>
<p>NIO：是基于缓冲区的，它里边有channel，selector，buffer，一个线程可以操作多个channel，nio之后一个线程可以干多个活，对网络编程是很重要的，能够提高并发量，高版本的tomcat都是使用的nio；</p>
<p>数据类型：</p>
<p>String、hash、list、set、zset；</p>
<p><strong>String</strong>：字符串，可以通过get\set方法存取，同时可以当做int类型使用，可以进行自增自减操作，可以选择作为自增主键，可以通过setnx实现分布式锁。我们项目中一般会把对象数据转为json格式然后缓存到redis，如果我们缓存的数据要经常更新的话选择hash去存储；</p>
<p><strong>Hash</strong>：（key，（key，value））/ 业务类型:业务id:xxxx = xx</p>
<p>类似于java中的hashmap存储若干的key,value，适合存储我们的对象数据，如果我们的数据要频繁修改我们选择用hash存储，如果仅仅是做缓存可以转json用string；hash好处：hash中存了若干的key,value，我们可以直接修改value不需要把整个的hash取回来，可以取指定的key对应的value。</p>
<p><strong>List</strong>：双向链表，他的左右都可以存取，可以当做消息队列去使用，现在我们有了kafka，rabbitmq很少去用list了，但是由于redis的性能比较高，可以做一些缓存操作，异步刷新数据库。</p>
<p><strong>Set</strong>：可以对两个set进行交集、并集、差集的操作，社交的应用，我的好友：1,2；他的好友有：2,3；可以求两个人的交集、并集</p>
<p>我的好友：friend:1 = [1,2];他的好友 friend:2 = [2,3]；</p>
<p><strong>Zset</strong>：跟set差不多，多了一个score（分值），可以进行排序；比如排行榜：房源浏览量排行（house:paihangbang = (房源id，分值)）</p>
<p>持久化机制：</p>
<p>避免宕机后数据丢失，redis有两种持久化机制：RDB和AOF</p>
<p>RDB：快照，容易造成数据丢失，他是根据key的更新数量和时间来决定持久化的。</p>
<p>AOF：是日志的方式，当有更新操作的时候会写日志到aof文件不会造成数据丢失，但是恢复数据比较慢。</p>
<p>在正式环境中两种持久化方式都是开启的，rdb是默认开启的，如果要开启aof我们可以通过修改redis.conf文件</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps78.jpg" alt="img">AOF</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps79.jpg" alt="img">RDB</p>
<p>常用命令：</p>
<p>string 的get\set，list的lpush\rpush，keys，bgsave</p>
<p>我们使用spring-data操作的redis，命令记得比较少</p>
<p>内存淘汰策略：</p>
<p>Redis为了保证可用性，当内存不足的时候有相应的淘汰策略，基本原则就是最近最少使用，一个是从有过期时间的key中执行这个策略，一个使用没有过期时间的key中执行，它还有随机淘汰但是没有什么价值一般不会使用，我们可以通过修改redis.conf文件制定淘汰策略</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps80.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps81.png" alt="img">通过这个修改淘汰策略</p>
<p>缓存击穿、穿透、雪崩：</p>
<p>击穿和穿透都属于缓存雪崩，雪崩其实就是缓存失效，程序一直在查缓存，但是缓存命中不了。</p>
<p><strong>穿透</strong>：缓存和数据库中都没有的数据，可用户还是源源不断的发起请求，导致每次请求都会到数据库，从而压垮数据库（用户攻击）。</p>
<p>解决方式：1.缓存一个较短的时间 2.布隆过滤器 3.非法请求直接返回</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps82.jpg" alt="img"> </p>
<p>布隆过滤器优缺点</p>
<p><strong>优点</strong>：优点很明显，二进制组成的数组，占用内存极少，并且插入和查询速度都足够快。</p>
<p><strong>缺点</strong>：随着数据的增加，误判率会增加；还有无法判断数据一定存在；另外还有一个重要缺点，无法删除数据。</p>
<p>对我们存储的key进行多次hash存入二进制数组中，默认是0如果占用的话变成1，当我们查询数据时候如果多次hash的值都是1就认为有数据，否则就认为没有数据，因为hash有冲突，他有误判的可能，减少误判可以进行多次hash，但是他会浪费数组空间（redis配置布隆过滤器）</p>
<p>我自己学习的时候安装配置过布隆过滤器，项目中生产环境不太清楚，开发环境是没有。</p>
<p><strong>击穿</strong>：Redis中一个热点key在失效的同时，大量的请求过来，从而会全部到达数据库，压垮数据库（<strong>缓存集中失效</strong>，热点数据缓存的过期时间要随机）。</p>
<p>解决方式：数据缓存时候用随机时间，避免集中失效</p>
<p>缓存一致性问题：</p>
<p><span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzYyMzQ2NTY1L2FydGljbGUvZGV0YWlscy8xMjU4MzIyNzQ=">使用Reids缓存信息，保证数据的一致性_王胖胖1112的博客-CSDN博客<i class="fa fa-external-link-alt"></i></span></p>
<p>无论是事务还是分布式系统的一致性问题，都分为：强一致性，弱一致性，最终一致性，其实要做到强一致性非常难，好多场景使用的都是最终一致性。</p>
<p><strong>同步方式</strong>：代码（先查缓存，如果有直接返回数据，没有再查数据库进行缓存）利用binlog日志 <strong>canal</strong> </p>
<p>话术：</p>
<p>同步写：先更新数据再更新redis，如果redis报异常数据库通过事务可以回滚。如果更新的过程中数据库完成了更新，redis还没更新，有其他线程来查询数据库，也会出现短暂的不一致。如果对数据要求一致性比较强的时候我们可以使用<strong>双删</strong>来处理，更新数据之前删除redis中的数据，更新数据之后删除redis中的数据，来保证数据的一致性。</p>
<p>Redis分布式锁：</p>
<p>分布式锁的核心：加锁和释放锁，redis通过setnx加锁，通过超时时间避免死锁，当我们执行业务完成之后会释放锁（删除redis中的key，锁库存：lock:商品id = null（可以存锁的唯一id））；</p>
<p>存在的问题就是：</p>
<p>锁超时，会造成无锁状态；系统忙与不忙接口的响应时间是不一样的，我们无法设置一个洽当的超时时间，需要有线程去监控锁，对锁延时。</p>
<p>锁误删</p>
<p>Redis加锁和设置锁超时是两个命令，保证不了原子性需要通过lua脚本来保证原子性。</p>
<p>Redisson框架（客户端）：redis客户端已经帮我们实现了以上的逻辑，可以对锁进行延时（续命），我们项目中尝试用过redisson分布式锁，由于环境比较复杂，我们用mysql乐观锁比较多一点。</p>
<h2 id="Mq（rabbitmq-kafka）异步、解耦、消峰"><a href="#Mq（rabbitmq-kafka）异步、解耦、消峰" class="headerlink" title="Mq（rabbitmq\kafka）异步、解耦、消峰"></a><strong>Mq（rabbitmq\kafka）异步、解耦、消峰</strong></h2><p><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uY24vcG9zdC82ODk2NzQ0OTAxNjY1NTIxNjc3">MQ消息队列（应用场景，产品对比） - 掘金 (juejin.cn)<i class="fa fa-external-link-alt"></i></span></p>
<p>项目中用到mq的场景比较多，比如：短信验证码的发送，邮件通知，服务之间的异步调用，他能极大的提高系统的吞吐量，接口的响应时间，他能起到解耦的作用；我们现在都是微服务的方式去开发的，我们项目中有短信服务，邮件服务，业务的各种微服务，微服务的特点就是：高内聚，低耦合，服务之间通过接口去调用，通过mq起到一个异步、解耦的作用。Mq还可以缓冲请求，把请求压到mq队列，起到流量消峰的作用。</p>
<p>重复消费：</p>
<p>消息进入消息队列之后被消费者消费多次的情况。我们是通过redis 的setnx命令解决的重复消费的问题，我们发送消息的时候有个唯一的消息id，消息消费的时候通过setnx命令来拦截消息，一般设置过期时间是24小时或者更多。</p>
<p>消息丢失（消息补偿）：</p>
<p><span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxMzMwOTgzNzc4L2FydGljbGUvZGV0YWlscy85OTYxMTE5Mw==">Spring Boot整合——RabbitMQ配置消息确认回调、消息转换以及消息异常处理<i class="fa fa-external-link-alt"></i></span></p>
<p><strong>消息在发送</strong>过程中消息中间件都是有回调的，可以通过回调来确保消息是一定<strong>发送成功</strong>的。消息发送到mqserver之后，mqserver也会存储消息做相应的做持久化。</p>
<p><strong>消息消费：</strong>消息的提交方式：自动提交（消费完成之后直接从消息队列中自动删除了）、手动提交（在完成业务逻辑之后进行手动提交（ack）才能从队列删除）如果消息没有正常消费，那么不会从队列删除。手动提交模式，消息的效率会大大降低，我们通过日志的方式来防止消息丢失的，在发送消息的时候向redis存了消息日志（消息内容、消息发送时间），消费完成之后会从redis删除，如果超过一定时间没有删除，定时任务会做补偿。</p>
<p><strong>顺序消费</strong>：</p>
<p><strong>消息堆积</strong>：</p>
<p>堆积的原因：消费者消费能力不足，或者是出错，通过增加多个消费者；消费者有推和拉两种模式，如果系统忙的话使用拉的模式比较好，如果不忙的时候，用推的效率会高一点。</p>
<p><strong>Rabbitmq的消息机制</strong>：</p>
<p>Rabbitmq有生产者、消费者还有交换机和队列，当生产者发送消息的时候先发送给交换机，交换机路由到队列，然后由消费者去消费消息；</p>
<p>我们在项目中发送消息的时候需要指定交换机的名字和路由key，如果发送的路由key和 交换机和队列绑定的路由key匹配的时候会路由到相应的队列去，扇形交换机和路由key没有关系。</p>
<p>Rabbitmq有很多种交换机，比如有直连的交换机、扇形的交换机、还有主题的交换机等等，只有扇形交换机和路由key是没有关系的。</p>
<p><strong>直连交换机</strong>：发送的路由key和绑定的路由key严格匹配才会路由到对应的队列。</p>
<p><strong>扇形交换机</strong>：他和路由key没有关系哪个队列跟它关联，它就会发送到对应的队列，其实就是一个订阅模式。</p>
<p><strong>主题交换机</strong>：支持 * # ，在交换机和队列绑定的时候路由key支持模糊匹配 * 匹配一个 #匹配零个或多个。</p>
<p>*（星号）可以替代一个单词。</p>
<p>＃（hash）可以替换零个或多个单词。</p>
<p>我们在项目中使用直连的还有扇形的比较多，特别是扇形的交换机可以帮助我们项目解耦，使用特别方便。</p>
<p>Kafka架构原理：</p>
<p>Kafka支持主题模式，我们在创建主题时候可以指定分区和副本，消费者可以消费指定分区的数据，一个分区的数据可以有多个消费者去消费，项目中我们是通过zookeeper搭建的集群，zookeeper是3个结点，kafka也是3个结点</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps83.jpg" alt="img"> </p>
<p>Kafka为什么快：<span class="exturl" data-url="aHR0cHM6Ly96aHVhbmxhbi56aGlodS5jb20vcC8xNDcwNTQzODI=">面试问：Kafka 为什么速度那么快？ - 知乎 (zhihu.com)<i class="fa fa-external-link-alt"></i></span></p>
<p>Kafka与rabbitmq的区别：</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps84.jpg" alt="img"> </p>
<p>\1. 吞吐量不同kafka（每秒10万）比rabbitmq（每秒万级）高一个级别</p>
<p>\2. Kafka只支持主题模式，rabbitmq支持多种交换机</p>
<p>\3. Rabbitmq是通过quenes来做消息存储，kafka是把消息存入磁盘这也是</p>
<p>\4. Kafka的数据写到磁盘中，不会造成消息丢失。Kafka占用的内存小</p>
<p>kafka吞吐量大的原因</p>
<p>我们项目中使用到的有rabbitmq和kafka我们业务处理使用的rabbitmq比较多也使用过kafka，我们的日志系统是同过kafka+elk完成的。</p>
<h2 id="Es"><a href="#Es" class="headerlink" title="Es"></a><strong>Es</strong></h2><p>介绍：</p>
<p>Es是一个企业级的搜索服务，大量的应用在项目中，他提供了restful和tcp的接口供客户端使用；他是基于Lucene开发的，lucene其实是一个工具包，由于搜索业务通用性强，所以出现了solr、es这样的通用搜索服务。</p>
<p>应用场景：</p>
<p>搜索体验、搜索性能 对标mysql，对mysql大数据量查询的补充，es是支持天然集群的</p>
<p>财务报表、商品搜索、旅游线路</p>
<p>Es集群的分片和副本：</p>
<p>一般是有3个集群节点，三个分片，数据分片存储，三个分片存储在三个节点上，每个分片的副本在其他的两个节点上都有，也就是在任意一个节点上都能查到es的所有数据</p>
<p>在存储数据的时候，可以把请求发送到任意一个结点上，结点会把请求发送给主节点，主节点处理请求，然后写主分片，同步副本</p>
<p>在查询数据的时候，可以发送到任意一个结点上，结点查询分片中的数据，聚合分片数据，进行排序、分页等</p>
<p>集群中分片数量并不是越多越好，如果分片太多的话，查询性能会出现问题</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps85.jpg" alt="img"> </p>
<p>客户端：查询</p>
<p>Spring-data，Java REST Client，在项目中使用的都是这两种客户端，我们使用spring-data操作es，他实现了elasticsearchRepository接口，可以完成简单的增删改查，rest Client做复杂查询更有优势（分组聚合）</p>
<p><span class="exturl" data-url="aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3M/c3JjPTExJnRpbWVzdGFtcD0xNjU4MzY1OTI0JnZlcj0zOTMzJnNpZ25hdHVyZT00Nll0bVJqVk1RR3hnTnRQZ3pEckpQQzVZbjg4NkRHU0FXelJHVkc1Ym9CWC16QjRCMzNHQnZUYzlobWtFNmtRMGwqeWhEWW9aa2lMT2lGM1dhS2pqU2NDaTJXNHNsdUQxMHJvQmM2N1VmRy1HYUx4S1g4amZtSzNXY3dXTHYyViZuZXc9MQ==">ES查询语法 (qq.com)<i class="fa fa-external-link-alt"></i></span> 模糊查询、分组、聚合</p>
<p>倒排索引（正排索引）：</p>
<p>其实就是词和文档建立了关系，形成了一个倒排表，当我们向索引库添加一个文档的时候，首先对文档进行分词，然后词和文档建立关系，我们搜索的时候是通过词直接定位到文档，不需要像正排索引那样逐篇文档扫描，这就是es快的原因。</p>
<p>[Elasticsearch数据同步](#:~:text=使用logstash-input-jdbc插件读取mysql的数据，这个插件的工作原理比较简单，就是定时执行一个sql，然后将sql执行的结果写入到流中，增量获取的方式没有通过 binlog 方式同步，而是用一个递增字段作为条件去查询，每次都记录当前查询的位置，由于递增的特性，只需要查询比当前大的记录即可获取这段时间内的全部增量，一般的递增字段有两种，AUTO_INCREMENT 的主键 id,和 ON UPDATE CURRENT_TIMESTAMP的update_time字段，id字段只适用于那种只有插入没有更新的表，update_time 更加通用一些，建议在mysql表设计的时候都增加一个update_time字段。)</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps86.jpg" alt="img"> </p>
<h2 id="Nginx"><a href="#Nginx" class="headerlink" title="Nginx"></a><strong>Nginx</strong></h2><p>Nginx是一个web服务器，可以配置反向代理、负载均衡，虚拟主机（域名vue项目部署），他是一个多进程的服务，主进程来接受请求转发给子进程进行处理。Nginx有一个核心配置文件叫nginx.conf,它里边可以配置多个server来实现反向代理，负载均衡。</p>
<p>（和tomcat是一样的都是web服务器，nginx能实现的功能，tomcat是一个servlet容器，专门部署java项目，是java项目的运行环境）</p>
<p>可以配置的功能：跨域、设置客户端ip、重定向、黑白名单（ip）</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps87.jpg" alt="img"> </p>
<p>反向代理：</p>
<p>我们在nginx中可以配置多个server，在server中有location，可以映射路径，通过proxy_pass来反向代理我们的tomcat，如果要做负载均衡的话需要指定upstream模块，模块中包含多个server，指定服务的ip和端口，如果要指定权重可以使用weight关键字；如果要做主从、热备，可以通过backup关键字来指定一个server作为一个备份。</p>
<p>Nginx提供了一些负载均衡的算法，默认是轮询，还有权重，ip_hash，url_hash（根据url或者用户的ip做hash运算，负载均衡到不同的服务上，缺点：如果服务挂掉了，用户的请求不能负载到其他tomcat上去），</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps88.jpg" alt="img"> </p>
<p>权重</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps89.jpg" alt="img"> </p>
<p>Ip_hash</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps90.jpg" alt="img"> </p>
<p>热备</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps91.jpg" alt="img"> </p>
<h2 id="项目团队"><a href="#项目团队" class="headerlink" title="项目团队"></a><strong>项目团队</strong></h2><h3 id="项目组"><a href="#项目组" class="headerlink" title="项目组"></a><strong>项目组</strong></h3><p>我做的是一个本部的（外包开发）的项目，我们的团队大概有20号人左右，有项目经理，产品经理，前端（pc端、小程序、安卓、ios 一般是两三个），测试（1:3），我们后台开发人比较多一点，有十二三个，我们项目组的忙的时候也会从别的项目组外调（借人），项目开发阶段人比较多，项目持续了有四五个月的开发，后面的话一直进行功能的迭代，到现在为止项目的工作量应该很小，后端应该有两三个人去维护这个项目，由于前端被调走了，我也会做一些前段的工作，熟悉js，jquery，vue的使用，熟悉vue组件。</p>
<h3 id="前端"><a href="#前端" class="headerlink" title="前端"></a><strong>前端</strong></h3><p>Jquery获取dom（document）元素的几种方式：$(‘#id’)根据id去获取，$(‘.class’)是通过样式名去获取，也可以通过标签获取元素（div,table），动态修改页面会使用到.html方法</p>
<p>Vue核心是数据双向绑定，还有一些其他的比如：路由，状态管理vuex（相当于我们后端的session全局的状态，他可以跨组件使用vuex中的数据），我们现在vue编程很简单了，主要是通过axios发送get\post请求来获取后端数据然后赋值就可以了；数据双向绑定：表达数据变化，会封装到data数据中。如果数据变了，页面也会变，vue开发其实是操作js对象，数组。</p>
<p><strong>学习（非工作时间）</strong></p>
<p>平时非工作时间会浏览一些公众号，csdn，哔哩哔哩的视频，学习一些东西，比如vue就是我自学的，redis集群，es集群，kafka集群都自己学习过，项目中也有在用，基本是项目中用什么就学什么，比如项目中用到了kafka，我也会学一些其他的消息中间件；我也会去看一些书：spring实战，springboot编程思想，springcloudAlibaba微服务架构实战派。</p>
<p><strong>专业介绍1</strong></p>
<p>熟悉spring框架，了解ioc、aop的思想，熟练使用springmvc，spring的事务管理，持久层主要是用的mybatis，也是用过jdbcTemplete模板，jpa，数据库用的是mysql，MongoDB，redis，我们使用aop做过日志，权限校验，使用springboot通过自动装配封装通用模块，基于功能做了一些封装，比如权限模块，表单重复提交，大概是这样。了解springcloud的组件以及使用。</p>
<h3 id="专业介绍2"><a href="#专业介绍2" class="headerlink" title="专业介绍2"></a><strong>专业介绍2</strong></h3><p>我工作三年，都在XX公司，这个公司是一个外包（本部）公司，我们有一些外包或者驻场的项目，我们是一个中小外包公司，大概有100人左右，我们公司主要是跟国家电网，龙湖地产，京东等有外包关系，我们的老大在这些公司当过高管，我在公司做过的项目主要是驻场的比较多，和甲方的人一块工作，我最近最的一个工作，甲方有五六个人，外包有十来个，我在公司主要做后台开发，项目组每周都会做工作的梳理，根据人的能力以及对业务的了解然后做工作分配，每天基本都有一个例会来讲解新需求，每个人负责介绍自己所负责模块的需求，主要是存在的问题以及对内对外的一些接口的协调，我工作中一般会调到其他业务系统的接口，我也会给其他业务系统提供接口，其他的就是负责自己业务的开发，自己单独负责过模块的开发，从表结构设计到接口设计，自测，发布，配合测试解决问题、bug，还要做一些接口的性能优化、升级</p>
<h3 id="离职原因"><a href="#离职原因" class="headerlink" title="离职原因"></a><strong>离职原因</strong></h3><p>\1. 最近做的项目结束了，公司里还有一些其他项目，由于资金链的问题，也不得已暂停了，公司也有一些外地项目能做，但是需要去其他城市，所以现在决定来重庆，其实很早之前也想过，这边的朋友比较多一点，还有环境比北京都要好一些，薪资可能会低点，但是还是决定来重庆长期工作（男\女朋友\家人），之前在北京工作主要是考虑发展，想在大城市发展几年，北京的这个软件行业还是比较热的。</p>
<p>\2. 我在的公司是一个外包公司，公司项目最近两年越来越少，跟甲方的合作也都出现了一些问题，主要是因为疫情还有现在的一个环境造成的，我们公司之前大概有两三百人，最近的话，缩减到一百人左右了，项目排期很多人都出现了空档，如果没有实际的项目开发的话，我们超过半个月就按50%发放薪资，属于没有办法才去找一个新的工作，从公司角度来说也是没有办法，如果我现在在公司只能去一些短期的项目组所以考虑找一个新的工作，或者大的项目。</p>
<p>\3. 我之前主要是外派的工作，项目结束，外包清场了，岗位还是蛮多的，最近的话北京这边我们公司没有外派岗了，公司迫不得已接了一些偏远地区的项目，而且是长期还没有做好心理准备（新疆\东北\南方），还是想在北京这边找一个工作，公司这边推荐的外派岗位也在面试，自己也在投简历找，希望能尽快找到工作。</p>
<p>\4. 由于疫情这两年都没有回家，刚好赶到上一个项目结束了，就回家待了一段时间（在家的话也是在处理公司的一些问题，属于居家办公，帮公司解决了一些项目问题，爸妈看我很累，就想让我在家多呆几天），公司在给我推荐岗位，我也在自己找。</p>
<h3 id="项目经验"><a href="#项目经验" class="headerlink" title="项目经验"></a><strong>项目经验</strong></h3><h3 id="线上问题"><a href="#线上问题" class="headerlink" title="线上问题"></a><strong>线上问题</strong></h3><p>生产环境有过接触，但是并不多，我们这有运维会给我们反馈一些问题（我们是外包、驻场开发的，一般线上问题都是甲方的人去解决的比较多）</p>
<p>一般线上问题由运维或者项目经理会把问题的截图，bug的一些情况发给我们开发团队（公司里有bug平台，然后把一些bug或者线上问题提交或者分发给我们），一般的问题，项目刚发布上线的时候问题提交多，一般都是配置的问题， 或者是空指针，业务数据不一致造成的，所以比较容易解决，还有比较常见的问题：服务调不通，或者是服务不可用这种情况一般是环境的问题，我有时候也会跟运维一块去看。</p>
<p>开发、测试、预发布环境、生产；（灰度发布：对部分用户进行功能升级）</p>
<p>比较复杂的问题，我们可以通过idea和线上联调（预发布环境），可以debug，因为有些错误很难重现，预发布环境和线上环境是最接近的环境。</p>
<p>我们有些项目是有elk这种日志系统，我们可以他来定位错误，他可以通过异常信息，接口地址，服务器ip，还有服务名称，时间区间来快速定位到错误，然后跟我们的控制台一样可以快速定位到哪行代码出的问题，能帮我们快速解决问题。</p>
<p>四方面：CPU、内存、磁盘、网络。</p>
<p>我遇到过的问题：<strong>磁盘</strong>空间用尽了，然后程序都会死掉，比如：有些日志一直在输出，占用磁盘空间，我们经常通过dockerfile生成镜像，都会占用大量的磁盘空间（linux加定时任务，定时清理一个月之外的日志，清理掉早期一些版本的镜像）</p>
<p><strong>内存</strong>：就是部署项目时候，有时候服务器不够用，一个linux上部署了太多的服务，造成资源不够用，我们会争取更多的服务器，合理的去分配线程的内存（问题：线上环境，项目是如何部署的）</p>
<p><strong>网络</strong>：流量太大，或者是网卡。我们是用的云服务器，是有带宽的，流量比较大的时候会造成带宽不够，请求的响应会出现问题，也会造成系统不可用。</p>
<p><strong>Cpu爆满</strong>：通过top命令可以定位到进程，能显示该进程的cpu和内存的使用情况，找到进程之后，一般会因为业务死循环或者频繁GC造成的cpu爆满，我们可以进一步通过jstack（jstack pid |grep ‘nid’ -C5 –color）找到响应的堆栈信息，通过vmstat查看上下文切换是否频繁。有时候我们可以通过命令把堆栈的信息文件下载下来，通过工具去分析，找到具体的原因。</p>
<p>Jvm调优：</p>
<p><span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdrZW5hbjg5MjgxOTUyNS9hcnRpY2xlL2RldGFpbHMvMTAyODQyNTcz">idea 远程联调配置_远程联调<i class="fa fa-external-link-alt"></i></span></p>
<h3 id="系统管理（认证、授权、鉴权）"><a href="#系统管理（认证、授权、鉴权）" class="headerlink" title="系统管理（认证、授权、鉴权）"></a><strong>系统管理（认证、授权、鉴权）</strong></h3><p>我们系统的权限管理是这样设计的，有用户表、角色表、权限表（菜单表）、角色权限关系表、用户角色关系表、用户权限关系表。我们可以通过角色给用户授权，也可以直接授权给用户。权限我们是保存在菜单里边，菜单分三级，一个是目录，一个是具体的菜单，还有一个功能按钮，他们对应这不同的权限编码，我们有系统管理专门负责增删改查。（部门、岗位，跟用户关联）</p>
<p>认证（也就是登录功能）：我们有一个认证服务，我们的项目支持这种用户名、密码、验证码登录（手机号验证码登录，用户端登录）我们项目是前后端分离的，采用token的方式来做的，当用户登录成功会给前端返回一个token，其他接口在调用的时候都会携带token，通过前端axios的拦截器放入head中，我们在网关（通过全局过滤器）对token做的校验，如果校验通过，路由到后端的服务；否则的话给前端返回错误编码401（未授权）；登录的时候我们是把请求的参数传到后台，做一些非空校验，判断用户密码或验证码是否正确，如果认证通过，我们会生成token缓存redis，redis存储了用户的token和用户的基本信息，以及权限。</p>
<p><strong>Token的生成</strong>：一种是jwt，jwt包含了用户的id，登录名称，唯一标识。另一种是我们可以自己生成token，用户的id + 用户的登录名 + 时间戳然后进行md5。</p>
<p><strong>授权</strong>：认证服务调用系统服务，根据登录名称查询用户的权限信息，缓存redis，当用户访问接口的时候，通过aop + 自定义注解做的权限校验。</p>
<p><strong>鉴权</strong>：aop + 自定义注解；我们有很多微服务都需要鉴权，我们封装了通用模块，写了自定义auth注解和aop的切面类，使用aop的环绕通知获取拦截方法的auth注解，然后读取权限编码；然后通过request工具类拿到token，然后从redis取出权限信息，然后校验当前接口的前权限编码与redis缓存的是否匹配，如果匹配失败直接返回一个校验失败的自定义异常（AuthExecption）。</p>
<p><strong>Token刷新</strong></p>
<p>Token 的过期时间是24小时（或一周）每天用户在第一次登录时候会拿旧的token去换一个新的</p>
<p><strong>Token被拦截（被盗）</strong></p>
<p>我们在登录的时候记录了客户端的ip和浏览器的类型，写了一些cookie，当用户在请求接口的时候会校验ip和登录时候的ip是否一致，ip如果不一致会让用户重新登陆</p>
<p><strong>SpringSecurity（shiro）</strong></p>
<p>是一个安全框架，他帮我们已经完成了认证、授权、鉴权的代码逻辑，我们 只需要做一些全局的配置（比如放行的接口路径，需要权限校验的接口，认证成功或者失败的时候配置一些handler），我们需要告诉他系统的用户是谁，以及怎么查询这个用户，他通过一些过滤器来实现权限的校验，各种功能的实现；常用的认证模式有用户名、密码认证，授权码登录（第三方登录，gitee是一个标准的单点登录）</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps92.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps93.jpg" alt="img"> </p>
<p>权限列表</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps94.jpg" alt="img"> </p>
<p>Userdetail接口实现的功能是用户名、密码、账号是否停用、状态（是否被锁），通过这个接口告诉SpringSecurity框架用户的关键信息</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps95.jpg" alt="img"> </p>
<p>userDetailServiceImpl接口告诉SpringSecurity框架如何去查询用户</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps96.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps97.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps98.jpg" alt="img"> </p>
<p>权限校验的注解</p>
<p><strong>表结构设计</strong></p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps99.jpg" alt="img">结合实际业务说道怎么设计出来的表，提到索引</p>
<p><strong>接口设计</strong></p>
<p>我和前端或者项目经理去沟通需求，我先根据某个需求的原型或者需求描述（需求文档），看看大致需要几个接口，然后再逐步确认每个接口的细节，先制定初版的接口文档包含：接口地址，出参、入参（apifox），这样前后端就可以同步开发，有问题再进一步沟通对接口进行调整。剩下的就是逐个的实现接口，会涉及到一些表结构的梳理或设计，为了让接口的性能高，有更快的响应时间，我们应该对接口有一些设计或者是业务逻辑的优化</p>
<p>问题：</p>
<p>具体接口场景：普通接口不需要去优化</p>
<p>\1. 接口实现过程中有服务之间调用，可能还有多个</p>
<p>​    其他接口的性能决定了你的性能，在必要的查询接口可以考虑是否加入缓存，对于更新接口是否做接口幂等性来应对超时（springcloud服务间调用间查询请求是做了重试的，更新请求一般不允许重试，如果重试需要幂等性解决）</p>
<p>\2. 接口实现过程中有大数据量</p>
<p>​    多线程或者redis缓存，或分批次查询数据</p>
<p>\3. 接口实现过程中涉及到安全</p>
<p>​    Feign：我们项目中服务之间调用有的时候权限的，我们有张表管理了appId和appKey当服务间调用的时候，调用方会读到配置，然后生成token，通过feign的RequestInterceptor拦截器将token放入head中，服务调用方去校验token</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps100.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps101.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps102.jpg" alt="img"> </p>
<p>​    第二种：通过httpclient工具类（hutool、okhttp）去发送get或post请求跟这个逻辑是一样的，接口文档中会有appId和appKey等调用接口的全局说明，一般会根据appId和appKey生成token 放入head中服务端去校验token</p>
<p>​    第三种：签名（支付）下单会调用第三方支付然后会有很多的参数，我们在项目中封装成一个实体类，在调用接口前需要对表单数据进行签名，我们会把参数类转化成map，遍历map将map中的key放入一个list中然后对他进行排序，然后在遍历list把他放入linkedHashMap（有序的）中，value从开始的map中取，然后把linkedHashMap的键值对拼接成字符串比如:a=1&amp;b=2…然后在加上appKey，进行md5签名</p>
<p><strong>4.</strong> <strong>具体的接口设计</strong></p>
<p>​    树结构的封装（一次性从数据库查出，然后进行递归）</p>
<p>​    我们项目中比如说菜单，省市区，分类，这种树结构的接口我们一般是通过一次性查库，然后通过递归去实现，这样就避免了循环查库，相当于是用空间换时间来提高查询速度；为了进一步提高接口的响应时间，我们还可以加缓存；其实我们可以把数据查出来直接给前端，让前端完成树的递归处理，这样极大的减轻了服务端的压力；其实我们开发接口时候还遇到了一个问题就是：树的层级不固定，遍历层级过多，会造成栈溢出，为了避免这种栈溢出，规定只能加载固定层级的树，或调整栈空间的大小</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps103.jpg" alt="img"> </p>
<p>部门树的层级是不固定的，记录了他的所有父id一直到顶级，给该列加索引可以通过like去查询，一次性可以查到他的所有下级部门。假如：你要查询102，</p>
<p><strong>大表优化</strong></p>
<p>项目中还遇到了一个问题：mysql的分页越大查询越慢，这种情况我们可以通过表的自关联查询先对id进行分页，然后再查询数据，也可以通过子查询的方式实现；</p>
<p><strong>select</strong> ***** <strong>from</strong> t5 <strong>where</strong> id**&gt;=**(<strong>select</strong> id <strong>from</strong> t5 <strong>order</strong> <strong>by</strong> text <strong>limit</strong> 1000000, 1) <strong>limit</strong> 10;</p>
<p><strong>select</strong> a.***** <strong>from</strong> t5 a <strong>inner</strong> <strong>join</strong> (<strong>select</strong> id <strong>from</strong> t5 <strong>order</strong> <strong>by</strong> text <strong>limit</strong> 1000000, 10) b <strong>on</strong> a.id**=**b.id;</p>
<p>比如说我们项目中id一般是有序的，他也代表了时间顺序，按照时间顺序做的分页，比如：订单，评论。这种分页的时候可以通过where id &gt; 100000再limit 10这样的查询性能也不受影响，这样的话每次前端都要传来一个最后查询的id；</p>
<p>还有就是一次查询的数据量比较大，比如在导出excel的时候可能需要几万条或者十万条之上的数据，这样一次从数据库查出来是比较慢的，我们可以先按照id进行分页，分页查询id然后在根据id去查询每个分页的数据，这样的话性能能提高很多，然后在结合多线程，性能能得到极大的提升。</p>
<p><strong>代码优化（可读性、性能）</strong></p>
<p>可读性：</p>
<p>工具类：通用工具类，业务工具类（租金计算），简化核心代码</p>
<p>全局异常，自定义异常，使核心代码没有那么多的try{}catch{}</p>
<p>通用模块的代码封装，提取公共业务逻辑。</p>
<p>命名规范，驼峰命名，命名一定要清晰，不能模棱两可，业务关键词的定义，我们项目中要求一件事情不要重复定义，比如表里已经定义了状态字段，或者是一些业务的名字，在代码层面也要去沿用，我们公司也有一些代码规范的文档，主要是参考了阿里巴巴的代码规范，做了一定的修改形成的。</p>
<p>如果一个方法的代码行数太多，要求我们对方法进一步封装，一般不会超过八九十行，主要是代码太长的话，可读性会变差，而且方法的职责可能不单一，复杂的代码逻辑我们都会进行一次封装（签名算法\表单重复提交）。</p>
<p>性能：</p>
<p>大表优化、多线程、接口优化、慢sql优化</p>
<h2 id="多线程使用"><a href="#多线程使用" class="headerlink" title="多线程使用"></a><strong>多线程使用</strong></h2><p>在一个接口需要查很多的数据，或者查询处理很多业务，我们可以使用多线程，我们自定义线城池，通过submit方法提交任务到线程池，然后他会返回futrue对象，我们把对象放入list，然后遍历list通过get方法获取返回值，futrue的get方法他是阻塞的，如果他没有执行完不会往下执行</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps104.jpg" alt="img"> </p>
<p>找个业务场景（数据大屏…）</p>
<p><strong>表单重复提交</strong></p>
<p>表单重复提交是一个通用需要，我们写到了通用模块中，自定义了@RepeatSubmit注解，里边定义了隔离时间，单位是毫秒，还有一些提示消息，当这个注解加到了controller接口上就能防止重复提交，我们是通过aop的前置通知来实现逻辑，我们是通过redis的setnx命令做的拦截，我们通过request工具类来获取当前url，在head中取出用户token，通过aop拦截到的方法参数，<strong>我们根据token + 表单的参数转成字符串通过md5工具类做一个签名，作为redis来的key</strong>，超时时间系统默认5秒。如果重复提交了，会抛出一个业务的异常，由全局异常拦截，提示给前端：“表单重复提交了，稍后再试”</p>
<p>这个过程中遇到的问题是需求不太清晰，中间做过多次的优化，一开始的时候仅仅用的url + 表单数据做的重复提交逻辑，其实这样时候如果不同用户提交了一个表单而且数据是一样的，是存在问题的，后来用了token加表单数据做为唯一标识，比如还有未登录的情况，我们会取出浏览器的信息，客户端的ip再加表单参数做的md5签名解决的。</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps105.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps106.jpg" alt="img"> </p>
<p><strong>短信验证码</strong></p>
<p>​    我们项目里边我发现短信验证码接口也挺复杂的，我看了他的接口，短信验证码接口大多数情况是没有权限的，可能存在恶意攻击，之前公司遇到了这种情况，恶意攻击会瞬间浪费很多短信，为了避免攻击，代码是这样写的，在获取短信验证码之前通过uuid生成了一个token缓存了redis，当用户在获取短信验证码的时候要提交 token到后台做校验，如果失败的话是获取不了验证码的，另外还会获取客户端的ip，浏览器信息，他会做md5签名，然后缓存redis，也会对签名进行校验，做避免攻击；我们还做了一个手机号短信发送次数的校验，我们设置的是一个手机号一天发送短信的上限，超过之后就不能再发送；做判断其实并不能解决用户攻击的问题，因为次数判断解决不了并发问题，这个我们给加了redis的分布式锁使用了redisson对token加锁，我们大概就是这么做的，在做的时候因为考虑的不是太清晰，也升级了很多次。</p>
<h2 id="字典数据"><a href="#字典数据" class="headerlink" title="字典数据"></a><strong>字典数据</strong></h2><p>我负责开发了字典数据业务功能模块的开发，主要有表设计，工具类的封装，服务间接口调用的封装，我们涉及两张表：一个是字典类型，一个是字典数据</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps107.jpg" alt="img"> </p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps108.jpg" alt="img"> </p>
<p>完成了增删改查的同时还提供了字典的工具类，主要是为了项目能够方便的使用字典数据，封装了一下方法，根据类型去查询字典数据列表，根据类型和名称去查询对应的value这些方法；因为工具类都是静态方法，不能直接注入ioc容器字典的service，我们使用spring的工具类从ioc中根据类型获取service，然后调用service的查询方法；同时我们还给字典数据加了redis缓存，提高了字典数据的性能；我们的项目中有个api模块对外提供了字典数据的访问接口。</p>
<p>在项目中前端也进行了封装，特别方便使用，当有新业务时候，只需要在后台加一些类型和数据，前端可以直接调到</p>
<h2 id="分布式事务"><a href="#分布式事务" class="headerlink" title="分布式事务"></a><strong>分布式事务</strong></h2><p>CAP理论base理论，两阶段提交，三阶段提交。</p>
<p>强一致性，弱一致性，最终一致性，这些都是分布式的一些理论，比如：java的uuid，mysql的雪花算法（强调数据库id的有序性，uuid如果作为主键的话是无序的，没办法进行主键索引），这种都是分布式系统唯一性的解决方法。</p>
<p>C（一致性）A（可用性）P（分区容错性），base理论说的是cap只能同时满足其中两个，像eureka是ap，dubbo的zookeeper强调的是cp；</p>
<p>一致性：数据的一致性，比如说，eureka各个节点之间存储的结点信息的一致性。</p>
<p><strong>可用性</strong>：集群避免单点故障，实现高可用。</p>
<p>分区容错性：大的项目为了高可用跨机房部署，防止断电、网络不通。</p>
<p><strong>强一致性</strong>：其实强一致性在系统中是很难做到的，像关系型数据库，我们是通过分布式事务来解决，非关系型数据库没有办法实现事务的要么成功，要么失败的，所以强一致性很难做到，而且代价可能也比较大。</p>
<p><strong>弱一致性</strong>：其实是允许分布式系统的数据可以在短时间内不一致，比如说一个房源发布了，mysql同步es是有延迟的，就属于弱一致性。</p>
<p><strong>最终一致性</strong>：意思是系统在某个时间点，或者很长时间内能达到一致性就可以了。比如：用户购买商品给会员加积分，由于这个操作是异步的通过mq完成，其实最终是否完成不能确定，我们可以通过mq的可靠性、重复消费来解决，只要最终给用户加上积分就可以了。</p>
<p><strong>两阶段提交</strong>：<a target="_blank" rel="noopener" href="https://www.jianshu.com/p/f2caa8737b7b">分布式事务 Seata(二) 理解什么是<strong>AT、TCC、Saga</strong> - 简书 </a></p>
<p><span class="exturl" data-url="aHR0cHM6Ly9zZWF0YS5pby96aC1jbi8=">Seata官网 <i class="fa fa-external-link-alt"></i></span></p>
<p>分布式事务<strong>seata</strong>：支持两阶段、三阶段提交、还有事务的一致性。</p>
<p>Seata事务的概念：事务管理器，事务协调者，分支事务</p>
<p>我们项目中用到过分布式事务seata，他其实用法很简单，我们需要启动一个seata-server服务，配置使用的注册中心还有事务日志的存储位置，我们可以使用mysql这种关系型数据来存储也可以直接存储在文件中，我们当需要分布式事务的时候，只需要在方法入口添加一个事务注解**@GloabTransactional**注解，就可以实现分布式事务；</p>
<p>原理：当事务执行的时候会通过事务管理器生成一个全局的事务id（xid），这个xid会传播到分支事务，分支事务的执行需要拿到全局事务锁，也就是根据xid去加一个分布式锁（通过mysql的悲观锁实现的），然后执行本地事务，也就是说分支事务的执行（feign）是有顺序的，其实加了分布式事务之后，服务间调用并没有任何改变，只是seata在这个过程中记录了增删改的日志（记录的是数据库修改前后的镜像，是以大字段的类型json格式存储的，解析sql生成json），存储到了mysql的undo_log表中，如果事务都能成功执行的话，就会删除日志表的数据也就是事务提交了，如果有任何一个分支事务执行失败，他都会根据日志信息回滚（通过json生成sql语句），他的回滚是一个异步的。</p>
<h2 id="大文件上传"><a href="#大文件上传" class="headerlink" title="大文件上传"></a><strong>大文件上传</strong></h2><p>大文件上传、断点续传（包括文件的上传和下载）、分片、token（文件签名或者md5签名）</p>
<p>大文件上传：为了应对网络不好，或者意外的故障造成文件不能一次上传文成，而且需要的时间由比较长，会造成重新上传一直失败的这种情况，其核心的话就是文件分片上传，分片也是实现断点续传的一个基础，上传一个文件的时候一般会对文件进行md5签名，为了防止生成的token可能是一样的我们一般会加一个时间戳进行md5，token的话就是文件的唯一标识，当分片上传的时候都会携带token的，用于区分上传的文件是属于哪个文件的，合并文件的时候也要根据token进行合并。</p>
<p>前端的话我们可以使用第三方的vue插件或者是云存储的插件或者是自己写js来完成分片，js有slice方法这个可以完成分片，为了实现更加快速高效的上传，前端的话可以同时上传几个分片，当时是最后一个分片的时候，他会调用后台的接口来执行文件的合并操作。</p>
<p>后端的话和普通的文件上传是一样的，只是他是分片上传的，从命名规范来说，每个分片是有序的，所以后缀都是 -1  -2  -3这样的名字当前台上传到最后一个分片的时候，会有一个所有分片都上传完的参数来告诉后端需要合并文件，合并文件它需要根据文件存储的目录读到该目录下的所有文件，对文件进行排序，逐一将所有分片文件写入一个文件中，如果有需要的话，给前端返回一个可访问的地址。</p>
<p>如果我们使用云存储的话，云存储其实已经提供了前端vue的插件以及demo，以及后端sdk的demo代码，读一下文档就可以完成。</p>
<h2 id="文件服务的封装："><a href="#文件服务的封装：" class="headerlink" title="文件服务的封装："></a><strong>文件服务的封装：</strong></h2><p>在我们的项目中是一个微服务，领导让我去完成这个服务实现文件上传的功能，使他变成一个通用服务，实现的功能有：本地上传、集成第三方云存储实现文件上传、还有大文件上传的功能，首先是写了一个接口来描述文件上传的需求，接口中定义了一个uploadFile方法，不同的实现方式对应着不同的实体类，我封装了一个工具类叫FlieUtils来实现文件操作的常用方法，使核心代码更加简洁，我们可以对外提供restful接口供其他服务使用，因为好多服务来调用文件服务，所以还封装了api接口，然后其他项目依赖之后就可以实现文件上传的功能，这里遇到了一个问题，我也忘记什么错了，反正就是上传不了，feign接口涉及到文件上传的服务间调用需要使用@RequestPart注解才可以。</p>
<p><a href="https://gitee.com/-/ide/project/y_project/RuoYi-Cloud/edit/master/-/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteFileService.java">若依文件上传/RuoYi-Cloud - Gitee IDE</a></p>
<p><strong>支付</strong></p>
<p>下单 回调 转账 掉单 签名 </p>
<p>我们项目对接过支付宝还有微信，我们是用支付宝或者微信都需要注册一个应用获得他的appid和appkey，wxkey，开通支付功能，他们都提供了resful的接口还有这种sdk的方式去调用他们的支付系统，主要涉及到的接口有下单接口，支付接口，转账之类的这种接口；</p>
<p>我们完成支付的话首先是调用下单接口，然后拿到第三方支付系统的订单id，可以通过这种二维码或者是跳转到第三方支付的页面，由用户来完成支付，当用户支付成功之后，然后把这个钱支付到第三方支付去了，我们可以在商户中心看到支付的结果，支付的结果必须通过回调（第三方支付）来获得支付结果，所以我们系统需要写一个回调接口，在接收到回调成功或者是失败的消息之后，然后会做相应的订单状态修改，以及一些业务逻辑处理（比方说做相应的通知，物流发货之类的）</p>
<p>下单参数：我们的appid，商户id，订单id，订单金额，回调地址</p>
<p>支付涉及到的技术点就是一个签名，无论是我们的下单接口还是回调接口，都是通过接口来保证安全的，签名算法是约定好的，我们去下单的时候微信会验证签名，回调的时候微信也会验证签名，具体的签名算法是这样的：</p>
<p><a href="https://gitee.com/-/ide/project/zghnzch/weixin-popular/edit/2.8.x/-/src/main/java/weixin/popular/util/SignatureUtil.java">https://gitee.com/-/ide/project/zghnzch/weixin-popular/edit/2.8.x/-/src/main/java/weixin/popular/util/SignatureUtil.java</a></p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps109.jpg" alt="img"> </p>
<p>微信或者支付宝的接口提供了这种sdk也提供了resful的接口，比如说微信，他有第三方的这种开源项目，对微信的这种接口都有过封装，我们拿过来直接用就行</p>
<p>掉单（30分钟未支付取消订单）：由于网络问题或者是服务宕机，接收不到这个第三方支付的回调结果，会造成用户实际已支付，然后订单和支付状态未修改的情况，为了防止掉单，我们可以通过定时任务检查未支付订单或者是通过mq延迟队列来解决；</p>
<p>微信第三方支付系统无论是支付成功还是失败，都会回调我们，如果我们没有响应success，他会一直回调我们，直到我们响应这个success，否则认为我们没收到消息，一般的话他会在一天之内回调（具体时间不清楚）调用的频率是逐渐递减的，比方说五分钟，后面改成十分钟或者十五分钟</p>
<p>我们项目里是用的mq的延迟队列来解决的，定时任务的解决会消耗性能（定时任务解决思路：30分钟扫描一次订单，如果支付成功，什么都不做，如果支付失败，删除订单，这样的话不能精确的控制订单的取消时间，误差可能是20分钟）延迟队列是用的rabbitMQ延迟队列来解决这种掉单问题（当下单的时候我们向mq里边发送一个消息，消息内容是有一个订单id，一定时间之后的话他会被消费，消费的时候就是根据订单id去查询订单状态，如果是已支付状态，就什么都不做，如果是未支付状态，我们调用第三方支付的查询接口来校验订单是否成功了，如果成功就修改订单状态）</p>
<p>前端等待支付结果而回调是异步的，不能及时响应结果给前端，这种情况下的话，前端用一个倒计时的方式，让用户等待，然后前端通过这种轮训方式请求后端，查看订单的支付结果（查询redis）如果订单成功跳转到订单详情，如果回调成功了会向redis写一个订单成功的标识。</p>
<h2 id="rabbitMQ的延迟队列（死信队列）"><a href="#rabbitMQ的延迟队列（死信队列）" class="headerlink" title="rabbitMQ的延迟队列（死信队列）"></a><strong>rabbitMQ的延迟队列（死信队列）</strong></h2><p>​    两种方式实现：一种是装插件的方式（聚合支付里边其实是用的插件）还有一种是利用mq的队列queue过期来实现，我们发送消息时的时候是把消息发送给交换机，交换机路由到队列，然后由消费者去消费的，如果一个队列没有消费者，他就是一个死信队列，如果队列设置了过期时间，过期的话就会从队列里删除，如果同时设置了交换机的名称还有路由key，就会转发到正式队列里面，然后被消费。</p>
<h2 id="Excel的导入导出"><a href="#Excel的导入导出" class="headerlink" title="Excel的导入导出"></a><strong>Excel的导入导出</strong></h2><p><strong>设备对接</strong><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps110.jpg" alt="img"></p>
<p>我们的项目属于物联网项目，他是一个分层架构的，包含应用层，管理层，数据层还有驱动层，应用层就是某个具体的功能接口，对应着我们pc或者是手机端的一些具体功能，管理层主要是用于设备管理，配置管理这些，数据层是设备数据收集入库，数据的一些查询接口，驱动层主要是对接硬件，涉及到一些协议，还有sdk的封装，主要是来控制设备或者是读取设备的信息。</p>
<p>设备的通信协议一般有mqtt，modbus，tcp，http其实也是支持的，设备可以通过这些协议链接到我们的微服务，不同的硬件设备调用方式是不一样的，如果和硬件设备的对接过程中涉及到的dll动态链接库把它放在jdk的bin下边，他其实就是c语言或其他语言写的一种接口，对应着我们jvm的本地方法栈，我们要调用他的话需要相应sdk的api和硬件进行通信，我大概就了解这么多吧，这一块做得比较少。</p>
<h2 id="Websocket应用"><a href="#Websocket应用" class="headerlink" title="Websocket应用"></a><strong>Websocket应用</strong></h2><p>我们在项目里的websocket应用比较多，比如后台管理系统的通知，还有的话就是一些直播场景（互动聊天）需要用到websocket通信，我们一般是通过ajax读取后台数据让页面变成动态的，他无法实现后台主动推送数据给前台，websocket可以，他是把http短链接改成长链接，能实现前后台双向通信，当这个用户登陆成功然后初始化页面的时候会建立websocket链接，后台可以给前台推送消息了；<img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps111.jpg" alt="img"><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps112.jpg" alt="img"></p>
<p>前后台对应的方法：onOpen建立链接，onClose关闭链接，onMessage发送消息，onError报错异常，我们在open方法用到了ConcurrentHashMap来保存客户端实例，当发送消息的时候我们拿到这个ConcurrentHashMap找到对应的用户调用这个当前实例的message方法给前端推送消息</p>
<p>消防报警：硬件在发生烟感设备报警的话，我们的MQ会接收到这个消息，消息类型就是火警，会调用websocket发送消息方法，然后让前端有警报，系统也会做相应的通知，由消防人员确认真实性，执行相应的处理措施    </p>
<h2 id="分布式定时任务xxl-job"><a href="#分布式定时任务xxl-job" class="headerlink" title="分布式定时任务xxl-job"></a><strong>分布式定时任务xxl-job</strong></h2><p>   实现方式常用的有Quartz，xxl-job,spring task,我们项目都有应用过，一般有一个后台管理界面来管理定时任务，对定时任务进行增删改查，定时任务主要是有这种调度器，执行器，触发器（cron）组成的，我们项目里用xxl-job比较多，分为调度器和执行器，调度中心是一个单独的服务，可以启动多个实例来实现高可用，然后他管理任务（任务的名称，调度算法可以选择轮训，选择第一个或者是最后一个）还有执行器，是嵌入在微服务里边，当微服务启动的时候他会把自己注册到注册中心去，当有任务触发的时候就会调这个执行器，xxl-job的好处是调度中心和执行器分离，减少了定时任务和具体的微服务之间的耦合。</p>
<h2 id="工作流"><a href="#工作流" class="headerlink" title="工作流"></a><strong>工作流</strong></h2><p>我接触过的工作流框架是activty，当然还有其他很多工作流框架，工作流框架在工作中有接触但了解不多，主要是应用于一些审核的场景，或者是大的像银行，联通移动营业厅会用到比较多，我理解的工作流的话就是有开始有结束，中间有若干个节点，每个节点都有对应的角色或者具体的审核人或者是业务的办理人，每个节点都会根据业务的变量或者审核的结果会流转到下一个节点或者跳过某节点，或者流程结束，或者会被驳回，他每个节点都会涉及到业务变量，其实是根据业务变量或者是根据状态进行驱动工作流到哪一个节点，大概就是这样。</p>
<p>我们公司里有专门的人负责工作流这个模块，然后我们在开发过程中也可能会用到工作流，主要是会调到工作流的启动接口还有审核接口，来告诉工作流是哪一个业务，哪个业务开始启动，当审核的时候也会调到工作流，把这个表单信息入库，驱动工作流调工作流接口</p>
<p>好多公司的工作流框架是自己研发的，没有用开源的工作流框架。</p>
<h2 id="Git"><a href="#Git" class="headerlink" title="Git"></a><strong>Git</strong></h2><p>我们项目中开发使用的git工具做的版本管理，我们有一个master主分支作为上线分支，我们在开发过程中根据任务的分配会拉一个或多个分支，主要是根据业务范围和上线时间来决定的；上线的前一天或者两天会合并到主分支，由测试进行最后的验证，如果没有问题就上线了；我们开发的时候每完成一个功能都会提交到本地仓库或推送到远程仓库，根据实际情况我们会多次提交本地，一次推送远程；如果功能开发完成的话，我们会尽早提测，和测试对接，修改一些bug，或升级一些功能做一些性能调优的功能，如果没有问题就等待上线。</p>
<p><strong>常用命令</strong>：</p>
<p>我们项目中常用的命令一般是：git pull 每天上班拉最新的代码，git add 、git commit将代码提交到本地仓库，通过git push 将代码推送到远程仓库；还有的话就是合并分支 git merge，一般会从个人分支到开发分支或者主分支，还有的就是版本的回退git reset -hard 版本号，还有查看历史记录 git history；我开发过程中用的idea的git插件或者Sourcetree，命令也会用到，但是不多。</p>
<h2 id="Maven"><a href="#Maven" class="headerlink" title="Maven"></a><strong>Maven</strong></h2><p>Maven是构建、编译、打包、部署项目的一个工具，他管理了maven仓库以及项目的依赖；</p>
<p>Maven虽然有很多功能，我们使用它管理依赖和版本，用到他的父子项目，父项目管理依赖和版本，在子项目使用的时候就不用指定版本号了</p>
<p>我们在项目中可能需要安装一些本地jar包到maven仓库，也可以把我们的jar包发布到公司的私服上，我们公司现在其实就有个maven私服（跟阿里云的、默认的仓库都是一样的，就是我们有了修改权限，公司没有外网，我们需要自己发布jar包，有些私服没有的jar包，我们需要自己去下载然后发布到私服），我们可以发布jar包到上边去。</p>
<p>Maven解决冲突时候有个mavenHelper插件，通过这个插件可以方便的去查看依赖的不同版本，可以直接跳转到maven的pom文件中进行排除exclusions。</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps113.jpg" alt="img"> </p>
<h2 id="Linux"><a href="#Linux" class="headerlink" title="Linux"></a><strong>Linux</strong></h2><p>使用的是Centos7版本，生产环境也是，我们开发环境内网是使用的公司内部的服务器，服务器中安装了各种软件，也就是开发环境。生产环境一般使用的是云服务器，一些比较重要的数据我们会使用机房管理；</p>
<p>常用命令：目录操作相关的命令：cd、mv、pwd、mkdir、cp -r（递归）还有就是vim编辑修改一些配置文件，常用的还有ping命令 ping服务器之间是否通，还有telnet ping服务器的端口是否通；还有的话就是ps 查看进程号，java的进程可以使用jps，杀死进程使用kill，kill -9 pid 立即杀死，kill -15 pid 建议杀死，一般的话我们会在kill -15后 停 2-3秒再去执行 kill -9 主要是因为这个进程的好多线程还在运行状态，直接使用kill -9 会使线程中断，业务没有执行完成。还有的话就是权限相关的命令，主要就是可以通过命令修改这个文件的所属组，还有读写执行权限，chown、chmod ；421–&gt; 读、写、执行 rwx ；还有一些系统命令，比如top命令可以查看系统的负载，可以看5、10、15分钟的平均负载，各个进程对内存及cpu的使用情况，还有free -m 可以查看系统的空闲内存，df -h查看磁盘的使用情况（百分比），如果磁盘空间满了，那么项目运行是有问题的可能会死掉。软件安装：yum、rmi；防火墙：service firewalld star\stop\status</p>
<h2 id="Docker"><a href="#Docker" class="headerlink" title="Docker"></a><strong>Docker</strong></h2><p>是一个容器引擎技术可以安装在window或者linux系统上，我们安装了docker环境之后，像我们的mysql、redis、es 就可以通过docker去安装，特别方便，特别是我们在安装开发、测试环境的时候特别简单，生产环境的服务的安装，docker对系统的资源占用还是很少的，得到了广泛的应用。</p>
<p>基本概念：docker有镜像，仓库，容器，宿主机。我们通过这个docker pull 去拉取镜像，本地通过docker run 启动镜像，也就是容器，我们的docker的容器其实是运行在宿主机上，宿主机一般有自己的内网ip还有外网ip，宿主机也是有自己的ip的，它就是我们的一个最简版的linux系统。</p>
<p>Docker命令：docker run -p(指定映射端口号xxx:xxx) -v(挂载宿主机的磁盘目录，一般比如mysql，redis配置文件，数据的存储文件，redis的持久化文件都会挂载在宿主机) -e（环境变量，比如说mysql的账户密码）-d (后台方式运行) –name(容器名称 唯一的) 镜像名:版本号（没有版本号会默认是最新的）；<strong>docker容器的命令：</strong>docker logs 查看容器日志，docker exec -it 容器名称 bash (进入容器内部)，docker cp 命令 可以在容器和宿主机之间进行文件的copy。</p>
<p>我们对mysql启动的容器做了修改，我们可以通过docker build . 来保存成一个新的镜像</p>
<h2 id="Dockerfile"><a href="#Dockerfile" class="headerlink" title="Dockerfile"></a><strong>Dockerfile</strong></h2><p>我们可以通过dockerfile来构建我们自己的镜像，使用的是在dockerfile文件所在的目录执行 docker build . 就可以打成一个镜像到本地的docker仓库，通过from关键字来执定jdk1.8的镜像，通过copy关键字将本地jar包copy到容器内，EXPOSE声明端口，ENTRYPOINT要执行的命令</p>
<p><span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pob25nX2pheS9hcnRpY2xlL2RldGFpbHMvMTA4NjY4MTY2">Dockerfile参数详解_zhong_jay的博客-CSDN博客_dockerfile 参数<i class="fa fa-external-link-alt"></i></span></p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps114.jpg" alt="img"> </p>
<h2 id="Docker-compose"><a href="#Docker-compose" class="headerlink" title="Docker-compose"></a><strong>Docker-compose</strong></h2><p>是docker自带的容器编排工具，他通过yml文件来管理各个服务，指定镜像，容器名称，也就是docker run时候的参数可以通过yml来配置。Docker run启动的话有些命令记不住，使用这个就不需要记了。Docker-compose up -d。</p>
<h2 id="Docker部署java项目"><a href="#Docker部署java项目" class="headerlink" title="Docker部署java项目"></a><strong>Docker部署java项目</strong></h2><p>我知道的有两种方式：一种的话是通过Dockerfile的方式，这种方式不好的地方就是，项目部署的频率太高，在linux上会生成大量的镜像，会浪费一些磁盘空间，还需要使用定时任务去清理它。所以我们一般会把jar包挂载在容器内部，挂载到jdk1.8的容器上来启动项目，避免每次都创建镜像。</p>
<p><img src="/images/%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA/wps115.jpg" alt="img"> </p>
<h2 id="ELK日志收集（1-逻辑清晰2-熟悉linux操作以及项目部署）"><a href="#ELK日志收集（1-逻辑清晰2-熟悉linux操作以及项目部署）" class="headerlink" title="ELK日志收集（1.逻辑清晰2.熟悉linux操作以及项目部署）"></a><strong>ELK日志收集（1.逻辑清晰2.熟悉linux操作以及项目部署）</strong></h2><p>Elk搭建的时候我的工作也不是太忙，我就跟团队参与搭建了elk，我们用的是kafka + elk 来做的，还有我们这个跟项目完全没有耦合的，我们用了一个filebeat的插件，监控磁盘的日志文件，可以作为kafka 的生产者，将监控到的日志给到kafka，logstash作为kafka的消费者，然后把日志保存到es中，通过kibana去查看日志；我们的日志的索引文件是按天生成的，一天生成一个es索引文件，然后kibana可以对日志文件进行聚合，搜索的时候可以跨索引去查找日志信息。</p>
<h2 id="项目部署"><a href="#项目部署" class="headerlink" title="项目部署"></a><strong>项目部署</strong></h2><p>我们根据不同的项目，如果是单体项目因为比较简单，所以我们可能会手动部署或者通过shell脚本部署；如果项目比较大我们必须使用<strong>DevOps（自动化部署）</strong>比如jenkins、jpom、k8s之类，我们项目中使用的是Jenkins、jpom，k8s还没有使用过。k8s我了解到的是：他是一个很大的架构，他是用来部署，动态扩容服务的；jenkins其实也很简单，就是集成了git、maven、jdk的插件，通过界面的方式可以实现项目的自动发布，以前的话只有运维和开发可以部署，现在有了他，任何人都可以直接部署。他其实和我们手动部署或者使用脚本部署都是一样的，通过git去拉取指定的分支的最新代码，通过maven去打包，然后执行ps kill 命令执行服务重启这些。</p>

    </div>

    
    
    
        <div class="reward-container">
  <div></div>
  <button onclick="var qr = document.getElementById('qr'); qr.style.display = (qr.style.display === 'none') ? 'block' : 'none';">
    打赏
  </button>
  <div id="qr" style="display: none;">
      
      <div style="display: inline-block;">
        <img src="/images/wechatpay.png" alt="云记 微信支付">
        <p>微信支付</p>
      </div>
      
      <div style="display: inline-block;">
        <img src="/images/alipay.png" alt="云记 支付宝">
        <p>支付宝</p>
      </div>

  </div>
</div>


      <footer class="post-footer">

        
  <div class="post-widgets">
    <div class="wp_rating">
      <div id="wpac-rating"></div>
    </div>
  </div>


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2021/08/23/Arthas%E6%8E%92%E6%9F%A5jvm%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98%E7%9A%84%E5%88%A9%E5%99%A8/" rel="prev" title="Arthas排查jvm相关问题的利器">
      <i class="fa fa-chevron-left"></i> Arthas排查jvm相关问题的利器
    </a></div>
      <div class="post-nav-item"></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          
    <div class="comments" id="valine-comments"></div>

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E6%9C%80%E5%85%A8%E9%9D%A2%E8%AF%95%E7%AC%94%E8%AE%B0-%E9%99%84%E4%BB%A3%E7%A0%81%E7%85%A7%E7%9D%80%E8%AF%BB%E9%83%BD%E8%83%BD%E6%8B%BF%E9%AB%98%E8%96%AA"><span class="nav-number">1.</span> <span class="nav-text">最全面试笔记-附代码照着读都能拿高薪</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#Java%E5%9F%BA%E7%A1%80"><span class="nav-number">1.1.</span> <span class="nav-text">Java基础</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Hash%E5%86%B2%E7%AA%81"><span class="nav-number">1.1.1.</span> <span class="nav-text">Hash冲突</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#JDK1-8%E6%96%B0%E7%89%B9%E6%80%A7"><span class="nav-number">1.1.2.</span> <span class="nav-text">JDK1.8新特性</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%9B%86%E5%90%88%E7%B1%BB%EF%BC%88%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%EF%BC%89"><span class="nav-number">1.1.3.</span> <span class="nav-text">集合类（数据结构）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Hashmap"><span class="nav-number">1.1.4.</span> <span class="nav-text">Hashmap</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F"><span class="nav-number">1.2.</span> <span class="nav-text">设计模式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%9E%84%E5%BB%BA%E8%80%85%E6%A8%A1%E5%BC%8F%EF%BC%9A"><span class="nav-number">1.2.1.</span> <span class="nav-text">构建者模式：</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Java%E5%9F%BA%E7%A1%80%E4%B9%8B%E5%A4%9A%E7%BA%BF%E7%A8%8B"><span class="nav-number">1.3.</span> <span class="nav-text">Java基础之多线程</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E9%94%81"><span class="nav-number">1.3.1.</span> <span class="nav-text">线程锁</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#ABA-%E9%97%AE%E9%A2%98%EF%BC%9A"><span class="nav-number">1.3.2.</span> <span class="nav-text">ABA 问题：</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#AQS%EF%BC%88ReentrantLock%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86%EF%BC%89"><span class="nav-number">1.3.3.</span> <span class="nav-text">AQS（ReentrantLock实现原理）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Synchronized%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86"><span class="nav-number">1.3.4.</span> <span class="nav-text">Synchronized实现原理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8E%9F%E5%AD%90%E5%8C%85%E8%A3%85%E7%B1%BB"><span class="nav-number">1.3.5.</span> <span class="nav-text">原子包装类</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E6%B1%A0"><span class="nav-number">1.3.6.</span> <span class="nav-text">线程池</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Java%E5%9F%BA%E7%A1%80%E4%B9%8BJVM%E8%99%9A%E6%8B%9F%E6%9C%BA"><span class="nav-number">1.3.7.</span> <span class="nav-text">Java基础之JVM虚拟机</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Jvm%E5%8E%9F%E7%90%86%EF%BC%9A"><span class="nav-number">1.4.</span> <span class="nav-text">Jvm原理：</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#jvm%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B"><span class="nav-number">1.4.1.</span> <span class="nav-text">jvm内存模型</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8"><span class="nav-number">1.4.2.</span> <span class="nav-text">类加载器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Jvm%E5%88%86%E4%BB%A3%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6"><span class="nav-number">1.4.3.</span> <span class="nav-text">Jvm分代垃圾回收</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Spring%E6%A1%86%E6%9E%B6"><span class="nav-number">1.5.</span> <span class="nav-text">Spring框架</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#IOC"><span class="nav-number">1.5.1.</span> <span class="nav-text">IOC</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#AOP"><span class="nav-number">1.5.2.</span> <span class="nav-text">AOP</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#SpringCloud"><span class="nav-number">1.6.</span> <span class="nav-text">SpringCloud</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Mysql"><span class="nav-number">1.7.</span> <span class="nav-text">Mysql</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%B4%A2%E5%BC%95"><span class="nav-number">1.7.1.</span> <span class="nav-text">索引</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Sql%E4%BC%98%E5%8C%96"><span class="nav-number">1.7.2.</span> <span class="nav-text">Sql优化</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Mybatis-%EF%BC%88jdbc%E3%80%81jdbcTemplate%E6%A8%A1%E6%9D%BF%E3%80%81mybatis%E3%80%81mybatis-plus%E3%80%81jpa%EF%BC%89"><span class="nav-number">1.8.</span> <span class="nav-text">Mybatis （jdbc、jdbcTemplate模板、mybatis、mybatis-plus、jpa）</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Psql%EF%BC%88PostgreSQL%EF%BC%89"><span class="nav-number">1.9.</span> <span class="nav-text">Psql（PostgreSQL）</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#MongoDB%EF%BC%88%E5%9C%A8%E5%85%AC%E5%8F%B8%E5%BD%93%E5%81%9A%E5%85%B3%E7%B3%BB%E5%9E%8B%E6%95%B0%E6%8D%AE%E5%BA%93%E5%9C%A8%E4%BD%BF%E7%94%A8%EF%BC%8C%E6%9B%BF%E4%BB%A3mysql%EF%BC%89"><span class="nav-number">1.10.</span> <span class="nav-text">MongoDB（在公司当做关系型数据库在使用，替代mysql）</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Redis"><span class="nav-number">1.11.</span> <span class="nav-text">Redis</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Mq%EF%BC%88rabbitmq-kafka%EF%BC%89%E5%BC%82%E6%AD%A5%E3%80%81%E8%A7%A3%E8%80%A6%E3%80%81%E6%B6%88%E5%B3%B0"><span class="nav-number">1.12.</span> <span class="nav-text">Mq（rabbitmq\kafka）异步、解耦、消峰</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Es"><span class="nav-number">1.13.</span> <span class="nav-text">Es</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Nginx"><span class="nav-number">1.14.</span> <span class="nav-text">Nginx</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%A1%B9%E7%9B%AE%E5%9B%A2%E9%98%9F"><span class="nav-number">1.15.</span> <span class="nav-text">项目团队</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%A1%B9%E7%9B%AE%E7%BB%84"><span class="nav-number">1.15.1.</span> <span class="nav-text">项目组</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%89%8D%E7%AB%AF"><span class="nav-number">1.15.2.</span> <span class="nav-text">前端</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%B8%93%E4%B8%9A%E4%BB%8B%E7%BB%8D2"><span class="nav-number">1.15.3.</span> <span class="nav-text">专业介绍2</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%A6%BB%E8%81%8C%E5%8E%9F%E5%9B%A0"><span class="nav-number">1.15.4.</span> <span class="nav-text">离职原因</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%A1%B9%E7%9B%AE%E7%BB%8F%E9%AA%8C"><span class="nav-number">1.15.5.</span> <span class="nav-text">项目经验</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%BA%BF%E4%B8%8A%E9%97%AE%E9%A2%98"><span class="nav-number">1.15.6.</span> <span class="nav-text">线上问题</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%B3%BB%E7%BB%9F%E7%AE%A1%E7%90%86%EF%BC%88%E8%AE%A4%E8%AF%81%E3%80%81%E6%8E%88%E6%9D%83%E3%80%81%E9%89%B4%E6%9D%83%EF%BC%89"><span class="nav-number">1.15.7.</span> <span class="nav-text">系统管理（认证、授权、鉴权）</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%BD%BF%E7%94%A8"><span class="nav-number">1.16.</span> <span class="nav-text">多线程使用</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%AD%97%E5%85%B8%E6%95%B0%E6%8D%AE"><span class="nav-number">1.17.</span> <span class="nav-text">字典数据</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1"><span class="nav-number">1.18.</span> <span class="nav-text">分布式事务</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%A4%A7%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0"><span class="nav-number">1.19.</span> <span class="nav-text">大文件上传</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%96%87%E4%BB%B6%E6%9C%8D%E5%8A%A1%E7%9A%84%E5%B0%81%E8%A3%85%EF%BC%9A"><span class="nav-number">1.20.</span> <span class="nav-text">文件服务的封装：</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#rabbitMQ%E7%9A%84%E5%BB%B6%E8%BF%9F%E9%98%9F%E5%88%97%EF%BC%88%E6%AD%BB%E4%BF%A1%E9%98%9F%E5%88%97%EF%BC%89"><span class="nav-number">1.21.</span> <span class="nav-text">rabbitMQ的延迟队列（死信队列）</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Excel%E7%9A%84%E5%AF%BC%E5%85%A5%E5%AF%BC%E5%87%BA"><span class="nav-number">1.22.</span> <span class="nav-text">Excel的导入导出</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Websocket%E5%BA%94%E7%94%A8"><span class="nav-number">1.23.</span> <span class="nav-text">Websocket应用</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%88%86%E5%B8%83%E5%BC%8F%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1xxl-job"><span class="nav-number">1.24.</span> <span class="nav-text">分布式定时任务xxl-job</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%B7%A5%E4%BD%9C%E6%B5%81"><span class="nav-number">1.25.</span> <span class="nav-text">工作流</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Git"><span class="nav-number">1.26.</span> <span class="nav-text">Git</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Maven"><span class="nav-number">1.27.</span> <span class="nav-text">Maven</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Linux"><span class="nav-number">1.28.</span> <span class="nav-text">Linux</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Docker"><span class="nav-number">1.29.</span> <span class="nav-text">Docker</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Dockerfile"><span class="nav-number">1.30.</span> <span class="nav-text">Dockerfile</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Docker-compose"><span class="nav-number">1.31.</span> <span class="nav-text">Docker-compose</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Docker%E9%83%A8%E7%BD%B2java%E9%A1%B9%E7%9B%AE"><span class="nav-number">1.32.</span> <span class="nav-text">Docker部署java项目</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ELK%E6%97%A5%E5%BF%97%E6%94%B6%E9%9B%86%EF%BC%881-%E9%80%BB%E8%BE%91%E6%B8%85%E6%99%B02-%E7%86%9F%E6%82%89linux%E6%93%8D%E4%BD%9C%E4%BB%A5%E5%8F%8A%E9%A1%B9%E7%9B%AE%E9%83%A8%E7%BD%B2%EF%BC%89"><span class="nav-number">1.33.</span> <span class="nav-text">ELK日志收集（1.逻辑清晰2.熟悉linux操作以及项目部署）</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%A1%B9%E7%9B%AE%E9%83%A8%E7%BD%B2"><span class="nav-number">1.34.</span> <span class="nav-text">项目部署</span></a></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">云记</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">12</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">5</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://gitee.com/feege" title="GitHub → https:&#x2F;&#x2F;gitee.com&#x2F;feege"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">云记</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-area"></i>
    </span>
    <span title="站点总字数">217k</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
    <span title="站点阅读时长">3:18</span>
</div>

        
<div class="busuanzi-count">
  <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
    <span class="post-meta-item" id="busuanzi_container_site_uv" style="display: none;">
      <span class="post-meta-item-icon">
        <i class="fa fa-user"></i>
      </span>
      <span class="site-uv" title="总访客量">
        <span id="busuanzi_value_site_uv"></span>
      </span>
    </span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item" id="busuanzi_container_site_pv" style="display: none;">
      <span class="post-meta-item-icon">
        <i class="fa fa-eye"></i>
      </span>
      <span class="site-pv" title="总访问量">
        <span id="busuanzi_value_site_pv"></span>
      </span>
    </span>
</div>








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>

<script src="/js/bookmark.js"></script>




  
  <script>
    (function(){
      var canonicalURL, curProtocol;
      //Get the <link> tag
      var x=document.getElementsByTagName("link");
		//Find the last canonical URL
		if(x.length > 0){
			for (i=0;i<x.length;i++){
				if(x[i].rel.toLowerCase() == 'canonical' && x[i].href){
					canonicalURL=x[i].href;
				}
			}
		}
    //Get protocol
	    if (!canonicalURL){
	    	curProtocol = window.location.protocol.split(':')[0];
	    }
	    else{
	    	curProtocol = canonicalURL.split(':')[0];
	    }
      //Get current URL if the canonical URL does not exist
	    if (!canonicalURL) canonicalURL = window.location.href;
	    //Assign script content. Replace current URL with the canonical URL
      !function(){var e=/([http|https]:\/\/[a-zA-Z0-9\_\.]+\.baidu\.com)/gi,r=canonicalURL,t=document.referrer;if(!e.test(r)){var n=(String(curProtocol).toLowerCase() === 'https')?"https://sp0.baidu.com/9_Q4simg2RQJ8t7jm9iCKT-xh_/s.gif":"//api.share.baidu.com/s.gif";t?(n+="?r="+encodeURIComponent(document.referrer),r&&(n+="&l="+r)):r&&(n+="?l="+r);var i=new Image;i.src=n}}(window);})();
  </script>



  <script>
  if (CONFIG.page.isPost) {
    wpac_init = window.wpac_init || [];
    wpac_init.push({
      widget: 'Rating',
      id    : ,
      el    : 'wpac-rating',
      color : 'fc6423'
    });
    (function() {
      if ('WIDGETPACK_LOADED' in window) return;
      WIDGETPACK_LOADED = true;
      var mc = document.createElement('script');
      mc.type = 'text/javascript';
      mc.async = true;
      mc.src = '//embed.widgetpack.com/widget.js';
      var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(mc, s.nextSibling);
    })();
  }
  </script>

  
<script src="/js/local-search.js"></script>











<script>
if (document.querySelectorAll('pre.mermaid').length) {
  NexT.utils.getScript('//cdn.jsdelivr.net/npm/mermaid@8/dist/mermaid.min.js', () => {
    mermaid.initialize({
      theme    : 'forest',
      logLevel : 3,
      flowchart: { curve     : 'linear' },
      gantt    : { axisFormat: '%m/%d/%Y' },
      sequence : { actorMargin: 50 }
    });
  }, window.mermaid);
}
</script>


  

  

  


<script>
NexT.utils.loadComments(document.querySelector('#valine-comments'), () => {
  NexT.utils.getScript('//unpkg.com/valine/dist/Valine.min.js', () => {
    var GUEST = ['nick', 'mail', 'link'];
    var guest = 'nick,mail,link';
    guest = guest.split(',').filter(item => {
      return GUEST.includes(item);
    });
    new Valine({
      el         : '#valine-comments',
      verify     : false,
      notify     : false,
      appId      : 'QCF1KJ4ILmj73nVybNhgiHEJ-gzGzoHsz',
      appKey     : 'di34H8f5jhsb77nm4gtqtxQu',
      placeholder: "发表下你的看法!",
      avatar     : 'mm',
      meta       : guest,
      pageSize   : '10' || 10,
      visitor    : true,
      lang       : 'zh-cn' || 'zh-cn',
      path       : location.pathname,
      recordIP   : true,
      serverURLs : ''
    });
  }, window.Valine);
});
</script>

</body>
</html>
